본문 바로가기

코딩 내공 Project/IO&NIO 네트워크

[NIO] Buffer 부분 정리


버퍼는 운영체제의 커널이 관리하는 시스템 메모리를 직접 사용 할 수있다.

byte[]인 경우 jvm의 힙영역에 메모리가 할당되고 바이트 배열의 초기값이 시스템 메모리로 한번 복사 되어야한다.
이렇게 복사 뿐만아니라 운영체제 수준에서 제공해주는 dma, 가상 메모리, 메모리 맵 파일들의 기능을 사용할 수 없다.

position - 현재 위치
limit - 버퍼에서 실제로 어디까지 사용할지를 지정
capacity - 메모리 크기, 몇개를 넣을 수 있는가. intbuffer의 capacity 100이면 int형 100개, 초기값 -1
mark - reset() 시 돌아갈 현재 position 설정

* Buffer 클래스
public abstract class Buffer{

//위치값 리턴
public final int position();
public final int limit();
public final int capacity();

//위치값 설정
public final Buffer position(int newPosition);
public final Buffer limit(int newLimit);

public final Buffer mark(); //mark 할 현재 position 지정
public final Buffer reset();//mark 위치로

public final int remaining() ; // (limit - position) 현재position에서 limit까지 읽을 수있는 개수
public final boolean hasRemaining(); //읽을 것이 있는지
public abstract boolean isReadOnly(); //읽기 전용인지(추상)
}

*Buffer 의 메소드들
- clear() : 모든 속성값 초기화, 데이터는 변화 없음, mark 값 -1 초기화
- rewind() : position 속성 초기화, mark 값 -1, 읽었던 데이터를 다시 읽을때 사용
- flip() : limit을 position이 있던 위치로 설정 후 position을 0으로 이동, mark = -1
         버퍼를 재사용하기위해 clear()하고 데이터를 버퍼에 쓴후 flip()으로 재사용 범위를 지정한 후 버퍼에서 데이터를 읽음

- compact() : position에서 limit 사이이, 남아있는 데이터를 버퍼의 맨 앞에부터 순서대로 쓴다.
                    쓴 위치만큼 position을 이동하고 limit는 버퍼가 생성되는 시점, capacity와 동일하게 된다 mark = -1
    : 버퍼 안의 데이터를 남기없이 모두 전송하고 싶을 때
 buf.clear();
//만약, 읽을 데이터가 있다면 채널에서 버퍼로 데이터를 읽어들인다.
while(in.read(buf)>0 && buf.hasRemaining()){
   buf.flip();
   out.write(buf);
   //전송하지 못한 데이터가 있다면 버퍼의 맨 앞부분으로 모두 복사해서 다음 번에 전송되게 한다
   buf.compact();

}

- duplicate() : 복사본 생성(속성까지)
                    각 속성은 다를 수 있지만, 데이터는 같은 메모리 공간을 참조하기때문에 변경시 같이 변경
- slice() : 버퍼의 일부분만 복사, 복사된 것의 속성은 초기화, 데이터는 공유됨



*ByteBuffer 클래스
public abstact class ByteBuffer extends Buffer implements Comparable{
  //팩토리 메소드를 사용해서 만드는 방법
  public static ByteBuffer allocate(int capcity);

  //이미 존재하는 배열을 이용해서 만드는 방법
  public static ByteBuffer wrap(byte[] array);
  public static ByteBuffer wrap(byte[] array, int offset, int length);

  //유틸리티 메소드들
  public final boolean hasArray();
  public final byte[] array();
  public final int arrayOffset();
}
- 상대적, 절대적 읽기 쓰기
- 상대적 : get(),put() 후 position 변경
- 절대적 : position이 변경되지 않음
- 배열을 이용하여 한번에 값을 읽고 쓰기 할때
  : 사용 가능한 길이를 먼저 계산후 사용
 byte[] b = new byte[3];
  
  // 버퍼에서 얼마나 쓸 수 있는지를 계산..
  int size = buf.remaining();
  if (b.length < size) {
   size = b.length;
  }
buf.get(b, 0, size);
buf.put(b, 0, size);

- 생성
 : ByteBuffer buf = ByteBuffer.allocate(1000);
 : byte[] arr = new byte[1024];
   ByteBuffer buf = ByteBuffer.wrap(arr);

* allcocate(), wrap() 생성된 버퍼는 jvm 힙 영역에 저장되는 넌다이렉트 버퍼이다
넌다이렌트 버퍼에는 내부적으로 보조 배열이 있다.
hasArray()메소드로 true가 리턴됨을 알수 있다.

* 시스템 메모리를 사용할 수있는 다이렉트 버퍼는 ByteBuffer만 생성할 수 있다.
   자바에서 사용하는 바이트 배열은 시스템 메모리에서 사용하는 순차적인 바이트들의 집함이 아닌
   객체 내에 바이트들을 저장하고 있는 형태기 때문에 커널이 관리하는 시스템 메모리에 직접 저장할 수 있는 형태가 아님

* 다이렉트 버퍼 생성
 ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024);
boolean isDirect = directBuffer.isDirect();

- 채널과 네이티브 IO 루틴들을 이용가능
- 메소드 파라미터로 주어진 크기만큼의 시스템 메모리를 jni를 사용해서 할당
- 이 시스템 메모리를 래핑하는 자바 객체를 만들어 리턴


* 다이렉트 버퍼를 메모리에 로드하거나 릴리즈하는 것은 넌다이렉트 버퍼를 생성,제거하는 것에 비해 부하가 크다
  그러므로 다이렉트 버퍼는 일반적으로 성능에 민감하고 버퍼를 오랫동안 유지해서 사용할 필요가 있는 곳에서 사용
  그 외는 넌다이렉트 버퍼를 사용
* 넌다이렉트 버퍼를 채널의 타켓으로 설정 가능하지만 좋지않다

* byteBuffer는 다양한 형식으로 읽고 쓸수 있다.(int, long....)
  - position은 형식의 크기만큼 이동

* 바이트 순서: 자바는 빅인디언 
 ByteBuffer buf = ByteBuffer.allocate(10);
  System.out.println(buf.order());
  buf.order(ByteOrder.LITTLE_ENDIAN);
  System.out.println(buf.order());

* charBuffer 클래스 사용
 -  char 2바이트
 - 스트링 제어하기
 public class CharBufferViewTest {
 public static void main(String[] args) {
  ByteBuffer buf = ByteBuffer.allocate(100);
  //뷰 버퍼 생성
  CharBuffer cbuf = buf.asCharBuffer();
  cbuf.put("Hello World!");
  cbuf.flip();
  //toString 메소드로 버퍼 안의 데이터를 스트링으로 만든다.
  //toString 메소드는 포지션에 영향을 주지 않는다
  String s = cbuf.toString();
  System.out.println("Data:"+s);
  System.out.println("buffer position:"+cbuf.position());
  int start = 6;
  int end = 12;
  //버퍼안의 데이터 일부만을 charSequence로 가져온다
  CharSequence sub = cbuf.subSequence(start, end);
  System.out.println(sub.toString());
 }
}