Akashic Records

자바 nio - 3 본문

오래된글/Java

자바 nio - 3

Andrew's Akashic Records 2018. 4. 9. 11:19
728x90

3. 버퍼 기본 동작

Buffer 클래스류들을 살펴 보기전에 버퍼의 일반적인 동작에 대해 알아보자.

1. 버퍼의 기본 구조

버퍼는 시작과 끝이 잇는 일직선 모양의 데이터 구조를 가진다. 버퍼는 객체 생성시 크기가 결정이 되며 한번 결정된 크기는 절대로 변하지 않는다. 따라서 크기를 늘이고자 한다면 다시 객체를 생성해 주어야 한다. 기본형 데이터들은 데이터 유형에 맞게 버퍼 객체를 생성해서 조작을 하며 이들 클래스들의 공통된 동작을 위해 java.nio.Buffer 클래스가 존재한다.

Buffer는 데이터를 관리하게 위해서 position,limit,capacity라는 중요한 값을 가지고 있다.

  • position : 현재 읽거나 쓸 버퍼에서의 위치값이다. 인덱스 값이기 때문에 0부터 시작하며, 음수를 가지거나 limit보다 큰값을 가질 수 없다. 만약 position과 limit 의 값이 같아진다면 더이상 데이터를 쓰거나 읽을 수 없다는 뜻이 된다.

  • limit : 버퍼에서 읽거나 쓸 수 있는 위치의 한계를 나타낸다. 이 값은 전체 버퍼에서 얼마큼의 메모리 공간을 사용할 것인지를 지정하는 것으로 capacity 보다 작거나 같은 값을 가지며 음수 값은 가지지 않는다. 인덱스 값이기 때문에 0부터 시작하며 최초 버퍼를 만들었을때는 capacity와 같은 값을 가진다.

  • capacity : 버퍼가 사용할 수 있는 최대 데이터 개수(메모리 크기)를 나타낸다. 크기는 음수가 되지 않으며 한번 만들어지면 절대 변하지 않는다. 인덱스 값이 아니라 수량임을 주의하자,.

  • 그외...

  • mark : reset 메소드를 실행했을 때에 돌아오는 위치를 지정하는 인덱스로서 mark()로 지정할 수 있다. 주의할 것은 지정시 반드시 position 이하의 값으로 지정해 주어야 한다. position나 limit의 값이 mark 값보다 작은 경우,mark는 실행되지 않는다. mark()가 정의되어 있지 않은 상태로 reset 메소드를 호출하면 ,InvalidMarkException 가 발생한다.

각 값들의 관계는 다음과 같다.

0 <= mark <= position <= limit <= capacity

예를 들어 전체 크기가 10인 버퍼를 생성했다면 이를 다음과 같이 그림으로 나타내 본다면...,












0

1

2

3

4

5

6

7

8

9


(pos)

(limit) (capacity)


각 숫자는 버퍼의 인덱스를 나타내고 처음 버퍼가 생성되면 그 초기값은 position이 0, limit는 capacity 값과 같으며 capacity 는 버퍼의 전체 크기를 가진다.

버퍼에서는 데이터를 효과적으로 다루기 위해서 capacity 안에서 position과 limit을 적절히 조절하게 된다.따라서 이들 세 값들을 잘 다룬다면 버퍼는 사용하기는 아주 편리할 것이다.

2. Buffer류 클래스의 get/put(읽기/쓰기) 메서드

버퍼의 동작에서 가장 중요한 동작은 바로 데이터를 읽고 쓰기일 것이다. 이때 사용하는 것이 put()과 get()이다. 이 메서드는 모든 버퍼류 클래스에서 사용하는데 크게 상대적 get/put과 절대적 get/put이 있다. 우선 상대적 get/put을 설명한다.


1) 상대적 get/put(읽기/쓰기)

상대적이라고 하는 이유는 position의 위치에 상대적인 동작을 하기 때문인데 position이 limit를 향해 읽거나 쓴만큼 증가한다. 따라서 상대적인 get/put 동작을 버퍼에 하면 현재 position에 있는 내용을 읽거나 positon 위치에 데이터를 쓴 다음 position값이 증가한다. 이 값은 limit과 같을 때까지 증가한다. 일단 position값이 limit값과 같아지면 그 다음부터는 get/put 동작을 할 수 없고 이들 값을 다시 재설정하는 메서드를 사용해야 한다. 만약 position값이 limit값까지 증가했는데도 get() 메서드를 사용한다면 BufferUnderflowException이 발생하며 put() 메서드를 사용한다면 BufferOverflowException이 발생한다.


2) 절대적 get/put(읽기/쓰기)

절대적이라 함은 get/put 동작시 현재의 position에 영향을 받지 않는 상태에서 limit 한도내에서 절대위치의 값을 읽거나 쓸 수 있다는 뜻이다. get/put 메서드 중에 인덱스 값을 인자로 받는 메서드가 있는데 이들 메서드를 사용해서 절대위치의 값을 읽거나 쓸 수 있다.

한꺼번에 여러 개의 데이터를 get/put 를 할 수 있는데 메서드 인자로 해당 데이터 배열을 넣어주면 된다. 예를 들어 CharBuffer클래스인 경우, get(char[] c)로, ByteBuffer클래스인 경우 put(byte[] b)로 사용한다. 이것은 position에 대해 상대적인 동작을 하며 역시 인자로 인덱스값을 주는 경우 절대위치에서 조작을 할 수 있다.

==> get/put 동작은 get()과 put()메서드로 수행되는데 이 메서드는 Buffer 클래스의 하위 클래스류에서 모두 가지고 있다. 동작은 같으나 인자나 리턴형은 다루는 데이터형-클래스-에 따라 다르다.

3> 테스트하기

이제 버퍼를 사용하는 간단한 예제를 다루어 보자. 우선 버퍼를 사용하려면 버퍼를 생성해야 한다. 당연하다. 버퍼를 생성하는 것은 간단하다. 각 XXXBuffer 클래스이 가지고 있는 클래스 메서드인 allocate() 메서드를 사용하면 간단하게 버퍼를 생성할 수 있다. 이때 인자로 버퍼의 초기 크기를 지정해준다. 형식은 다음과 같다.

IntBuffer buf=IntBuffer.allocate(10);

이는 int형 데이터를 다루는 크기가 10인 버퍼를 생성한다. 이제 예제를 보자.

import java.nio.*;

public class BufferTest1 {

public static void main(String[] args) {

//크기가 10인 IntBuffer버퍼 객체 생성

//이때 초기값은 pos는 0, limit는 capacity 값과 같다.

IntBuffer buf=IntBuffer.allocate(10);

System.out.println(buf);

buf.put(11);

System.out.println(buf.get(0));

System.out.println(buf);

buf.put(12);

System.out.println(buf.get(1));

System.out.println(buf);

buf.put(13);

System.out.println(buf.get(2));

System.out.println(buf);

buf.get(7);

buf.put(14);

buf.get(9);

System.out.println(buf);

}

}


<< 실행 결과 >>


C\>java BufferTest1

java.nio.HeapIntBuffer[pos=0 lim=10 cap=10]

11

java.nio.HeapIntBuffer[pos=1 lim=10 cap=10]

12

java.nio.HeapIntBuffer[pos=2 lim=10 cap=10]

13

java.nio.HeapIntBuffer[pos=3 lim=10 cap=10]

java.nio.HeapIntBuffer[pos=4 lim=10 cap=10]


우선 버퍼 객체를 생성하고 이를 그대로 화면에 출력을 하면 position, limit, capacity가 차례대로 출력됨을 알 수 있는데 이는 toString()메서드 때문이다. 맨처음 출력은 버퍼의 초기값을 출력해 준다. pos는 0이고 lim과 cap는 값이 같다. 그리고 put()메서드를 이용해서 버퍼의 0번자리에 '11'이라는 값을 넣고 get()을 이용해서 출력한다. 그리고 나서 버퍼 객체를 출력하면 pos가 1칸 이동한 것을 알 수 있다. 그리고 총 5번의 put()메서드를 이용해서 값을 버퍼에 넣으니 pos가 put한 만큼 이동한 것을 알 수 있다. 결과를 보면 get을 하고 나면 pos는 이동하지 않는 다는 것을 알 수 있다. 이를 그림으로 나타내면... 다음과 같다.

11

12

13

14

0

0

0

0

0

0


0

1

2

3

4

5

6

7

8

9



(pos=4)

(limit,capacity=10)


Heap Buffer와 Direct Buffer

위의 예제 소스 결과에서 IntBuffer 객체를 출력하니 java.nio.HeapIntBuffer가 출력됨을 알 수 있다. 이는 버퍼 객체를 생성하면 메모리에 적절한 내부 구현 객체가 생기는데 allocate() 메서드로 생성하면 Heap Buffer를 얻을 수 있다. 이는 앞서 MappedByteBuffer 클래스 소개시 잠깐 언급을 했는데 Heap Buffer는 자바 가상머신내에 객체가 저장되는 메모리 공간인 Heap 버퍼를 말한다. 따라서 allocate() 메서드로 생성된 버퍼에서는 데이터가 Heap 버퍼에 저장이 된다. 하지만 이 공간이 아닌 일반 메모리 공간에 버퍼를 생성할 수 있는데 이를 Direct Buffer라 한다. 이 버퍼는 allocateDirect()메서드로 생성할 수 있다. Direct Buffer로 만들면 운영체제가 제공하는 메모리 입출력기능을 직접 쓰기때문에 입출력 속도가 매우 빠른 장점을 가지고 있다. 하지만 만들때에는 시간이 걸린다. 따라서 Direct Buffer는 대용량의 데이터를 다루고 오랜 기간동안 쓰면서 빠른속도가 필요한 곳에 적합하다. 단, Direct Buffer는 ByteBuffer 클래스만 사용가능하다.


728x90

'오래된글 > Java' 카테고리의 다른 글

자바 nio - 5  (0) 2018.04.09
자바 nio - 4  (0) 2018.04.09
자바 nio - 2  (0) 2018.04.09
자바 nio - 1   (0) 2018.04.09
Java theory and practice: 웹 티어의 상태 복제  (0) 2018.04.09
Comments