삶 가운데 남긴 기록 AACII.TISTORY.COM
NIO Buffer 본문
direct buffer / non-direct buffer
direct buffer는 운영체제가 관리하는 메모리 공간을 사용하는 버퍼이고, non-direct buffer는 JVM이 관리하는 힙 메모리 공간을 이용하는 버퍼입니다.
direct buffer가 버퍼의 생성은 느리지만 버퍼의 크기가 더 크고 성능이 더 높습니다.
생성시 allocateDirect() 메소드로 생성하면 direct버퍼로 생성되고 allocate()메소드로 생성하면 non-direct로 생성됩니다.
Buffer
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.IntBuffer;
public class BufferCapacityEx {
public static void main(String[] args) {
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(100);
System.out.println("용량:"+byteBuffer.capacity() + "byte");
CharBuffer charBuffer = ByteBuffer.allocateDirect(100).asCharBuffer();
System.out.println("용량:"+charBuffer.capacity() + "문자");
IntBuffer intBuffer = ByteBuffer.allocateDirect(100).asIntBuffer();
System.out.println("용량:"+intBuffer.capacity() + "자리 정수");
}
}
Big-endian / Little-endian
운쳥체제마다 이진수(byte)를 처리할 때 처리 순서가 다를 수 있습니다.
그래서 운영체제로 데이터를 보내거나 받을 때 처리 순서가 영향이 있을 수 있습니다.
이진수 배열 앞쪽 바이트 먼저 처리하는 것을 Big-endian이라고 하고 뒤쪽 바이트 먼저 처리하는 것을 Little-endian이라고 합니다.
자바의 JVM은 Big-endian으로 처리하지만 윈도우의 경우 Little-endian으로 처리합니다.
import java.nio.ByteOrder;
public class EndianEx {
public static void main(String[] args) {
System.out.println("운영체제:"+System.getProperty("os.name"));
System.out.println("엔디안:"+ByteOrder.nativeOrder());
}
}
direct 버퍼일 경우 운영체제의 native I/O를 사용하므로 운영체제의 기본 엔디안 순서로 JVM의 엔디안을 맞추는 것이 성능에 도움 됩니다.
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(100).order(ByteOrder.nativeOrder());
기본 개념
position: 현재 읽거나 쓰려고 하는 위치입니다.
인덱스 값이기 때문에 0부터 시작하며 limit보다 클 수 없습니다.
만약 position과 limit이 같아진다면 더이상 데이터를 읽거나 쓸 수 없습니다.
limit: position이 가질 수 있는 한계값 입니다.
capacity 값보다 클 수 없습니다.
최초로 버퍼를 만들었을 때는 capacity와 같은 값을 갖습니다.
capacity: 버퍼의 최대 데이터 개수(메모리 크기)입니다.
인덱스 값이 아니라 수량(크기) 입니다.
mark: reset시 돌아오는 위치를 지정하는 인덱스입니다.
반드시 position 이하 값으로 지정해줘야 합니다.
postion이나 limit값이 mark 값 보다 작아지면 mark는 제거됩니다.
mark가 없는 상태에서 reset()하면 InvalidMarkException이 발생합니다.
allocate(capacity): JVM의 힙영역에 할당합니다.
allocateDirect(capacity): 버퍼를 운영체제가 할당합니다. 운영체제를 이용하는 I/O작업이면서 할당과 해제가 잘 일어나지 않는 작업일 때 사용을 권고하고 있습니다.
포인터 반환 주요 method
int capacity(): 버퍼의 전체 크기를 반환 합니다.
int position(): 포인터의 시작 위치를 반환 합니다.
int limit(): 포인터의 마지막 위치를 반환합니다. 기본값은 capacity와 동일 합니다.
주요 method
clear(): position을 0 으로 설정하고 limit를 capacity값으로 설정하여 초기화 합니다.
flip(): position을 0 으로 설정하고 limit를 현재 버퍼에 저장된 내용의 마지막 위치로 이동합니다. read 후 write하기 전에 호출합니다.
hasRemaining(): position과 limit가 다르면 true, 같으면 false 반환합니다. 즉, 버퍼에 내용이 있는지 검사해서 내용이 있으면 true, 없으면 false를 반환합니다.
rewind(): position을 0으로 초기화 합니다. 이미 put한 것을 취소할 때 사용합니다.
wrap(byte[] array): 바이트 배열을 ByteBuffer로 변환합니다. capacity와 limit는 바이트배열과 같습니다.
compact(): position과 limit 사이 데이터를 버퍼 맨 앞쪽으로 이동시킵니다. 그 후 position을 이동한 데이터의 다음 위치로 이동하고 limit는 capacity값으로 변경시킵니다. 즉, buffer에 남은 데이터들을 앞으로 이동시키고 그 다음부터 데이터를 채울 수 있게 해줍니다.
put(byte): 버퍼에 byte 데이터를 입력합니다.
get(): 이전 get()을 이용해서 읽은 다음 부분부터 값을 읽습니다.
import java.nio.Buffer;
import java.nio.ByteBuffer;
public class BufferEx {
public static void main(String[] args) {
ByteBuffer buffer = ByteBuffer.allocateDirect(7); //7바이트 크기로 다이렉트 버퍼 생성
printState(buffer);
buffer.put((byte)10);
buffer.put((byte)11);
System.out.println("[2바이트 저장 이후]");
printState(buffer);
buffer.put((byte)12);
buffer.put((byte)13);
buffer.put((byte)14);
System.out.println("[3바이트 저장 이후]");
printState(buffer);
//버퍼 데이터를 읽기 위해 position 0으로 이동
buffer.flip();
System.out.println("[flip() 실행 후]");
printState(buffer);
buffer.get(new byte[3]);
System.out.println("[3바이트 읽은 후]");
printState(buffer);
//현재 position mark(표시)
buffer.mark();
System.out.println("-----현재 위치 mark-----");
buffer.get(new byte[2]);
System.out.println("[2바이트 읽은 후]");
printState(buffer);
//mark된 위치로 position 이동(reset)
buffer.reset();
System.out.println("[position을 mark 위치로 이동 후]");
printState(buffer);
//position을 0으로 이동
buffer.rewind();
System.out.println("[rewind() 실행 후]");
printState(buffer);
//모든 위치 속성값 초기화
buffer.clear();
System.out.println("[clear() 실행 후]");
printState(buffer);
}
public static void printState(Buffer buffer) {
System.out.print("position:"+buffer.position() + ",");
System.out.print("limit:"+buffer.limit()+",");
System.out.println("capacity:"+buffer.capacity());
}
}
import java.nio.ByteBuffer;
public class CompactEx {
public static void main(String[] args) {
System.out.println("[7바이트 크기로 버퍼 생성]");
ByteBuffer buffer = ByteBuffer.allocateDirect(7);
buffer.put((byte)10);
buffer.put((byte)11);
buffer.put((byte)12);
buffer.put((byte)13);
buffer.put((byte)14);
buffer.flip(); //데이터를 읽기 위해 위치(position)를 0으로 변경
printState(buffer);
buffer.get(new byte[3]);
System.out.println("3바이트 읽음");
buffer.compact(); //읽지 않은 데이터는 인덱스0부터 복사
System.out.println("[compact()실행 후]");
printState(buffer);
}
private static void printState(ByteBuffer buffer) {
System.out.print(buffer.get(0) + ", ");
System.out.print(buffer.get(1) + ", ");
System.out.print(buffer.get(2) + ", ");
System.out.print(buffer.get(3) + ", ");
System.out.println(buffer.get(4));
System.out.print("position:"+buffer.position() + ", ");
System.out.print("limit:"+buffer.limit() + ", ");
System.out.println("capacity:"+buffer.capacity());
}
}
버퍼 변환
채널이 읽고 쓰는 버퍼는 ByteBuffer이기 때문에 문자열 또는 다른 타입의 버퍼로 변환해야 합니다.
반대로 문자열 등을 채널을 통해 쓰고 싶다면 ByteBuffer로 변환해야 합니다.
ByteBuffer와 String 사이의 변환
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
public class ByteBufferToStringEx {
public static void main(String[] args) {
Charset charset = Charset.forName("UTF-8");
//String -> ByteBuffer
String data = "변환 테스트";
ByteBuffer byteBuffer = charset.encode(data);
//ByteBuffer -> String
data = charset.decode(byteBuffer).toString();
System.out.println(data);
}
}
ByteBuffer와 IntBuffer 사이의 변환
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.Arrays;
public class ByteBufferToIntBufferEx {
public static void main(String[] args) throws Exception{
//int[]->IntBuffer->ByteBuffer
int[] writeData = {10, 20};
IntBuffer writeIntBuffer = IntBuffer.wrap(writeData);
ByteBuffer writeByteBuffer = ByteBuffer.allocate(writeIntBuffer.capacity()*4);
for(int i = 0; i<writeIntBuffer.capacity(); i++) {
writeByteBuffer.putInt(writeIntBuffer.get(i));
}
writeByteBuffer.flip();
//ByteBuffer -> int[]
ByteBuffer readByteBuffer = writeByteBuffer;
IntBuffer readIntBuffer = readByteBuffer.asIntBuffer();
int[] readData = new int[readIntBuffer.capacity()];
readIntBuffer.get(readData);
//원래 배열 복원
System.out.println(Arrays.toString(readData));
}
}
728x90
'DEV&OPS > Java' 카테고리의 다른 글
NIO TCP 서버/클라이언트 채팅 프로그램 (0) | 2022.05.26 |
---|---|
NIO 파일 채널 (0) | 2022.05.25 |
NIO Path (0) | 2022.05.23 |
IO(블로킹)기반 자바 네트워킹 (0) | 2022.05.20 |
입출력 보조 스트림 (0) | 2022.05.19 |