본문 바로가기

Language/자바

Java Stream / marshal, unmarshal / serializable / NIO

http://zyint.tistory.com/11 참고

http://hyeonstorage.tistory.com/252 참고

http://eincs.com/2009/08/java-nio-bytebuffer-channel-file/ 참고


정리용으로 노트


1.1. 배경 및 정의

어떤 목표 지점에서 데이터를 읽어 들이고, 목표 지점에 기록하는 것을 데이터의 Input, Output 작업이라고 한다. 자바에서 사용하는 Input과 Output작업의 목표 지점은 아주 다양하다. 기본적으로 여러분은 모니터로 입출력하는 것을 배웠다. 가장 많이 사용하는 System.out.println에서 out은 Console 화면에 데이터를 출력하는 것을 담당하고 있다. 그리고 System.in은 키보드의 입력을 담당하고 있다. 곰곰이 생각해보면 데이터를 입력 받고 데이터를 출력하는 작업은 아주 까다로울 것이라고 생각하는데 의외로 여러분은 쉽게 이러한 입출력을 해결하고 있다. 그런데 in, out과 같이 입력과 출력을 도와주는 중간역할을 하는 것이 없다면 어떻게 해야 하는지를 한번 생각해 보자. 아마도 하드웨어적인 것까지 직접 프로그래머가 다루어야만 모니터에 뭔가를 출력할 수 있을 것이다. 입출력을 도와주는 중간 역할을 하는 것을 우리는 스트림이라고 부른다.



2. 마샬링 (marshalling) (Object to byte - ots)

마샬링(marshalling)은 데이터를 바이트의 덩어리로 만들어 스트림에 보낼 수 있는 형태로 바꾸는 변환 작업을 뜻한다.

자바에서 마샬링을 적용할 수 있는 데이터는 원시 자료형(boolean, char, byte, short, int, long, float, double)와 객체 중에서 Serializable 인터페이스를 구현한 클래스로 만들어진 객체이다.

객체는 원시 자료형과 달리 일정한 크기를 가지지 않고 객체 내부의 멤버 변수가 다르기 때문에 크기가 천차만별로 달라진다. 이런 문제점을 처리할 수 있는게 ObjectOutputStream 클래스이다.


3. 직렬화 (Serializable)

마샬링으로 바이트로 분해된 객체는 스트림을 통해서 나갈 수 있는 준비가 되었다. 앞에서 언급한대로 객체를 마샬링하기 위해서는 Serializable 인터페이스를 구현한 클래스로 만들어진 객체에 한해서만 마샬링이 진행될 수 있다.

Serializable 인터페이스는 아무런 메소드가 없고 단순히 자바 버추얼 머신에게 정보를 전달하는 의미만을 가진다.

일렬로 줄을 세워야 바이트 형태로 데이터를 주고받을 수 있음.

* 직렬화가 가능한 객체의 조건

(1) 기본형 타입(boolean, char, byte, short, int, long, float, double)은 직렬화가 가능

(2) Serializable 인터페이스를 구현한 객체여야 한다. (Vector 클래스는 Serializable 인터페이스구현)

(3) 해당 객체의 멤버들 중에 Serializable 인터페이스가 구현되지 않은게 존재하면 안된다.

(4) transient 가 사용된 멤버는 전송되지 않는다. (보안 변수 : null 전송)



4. 언마샬링 (unmarshalling) (byte to Object - bto)

언마샬링은 객체 스트림을 통해서 전달된 바이트 덩어리를 원래의 객체로 복구하는 작업이다. 이 작업을 제대로 수행하기 위해서는 반드시 어떤 객체 형태로 복구할지 형 변환을 정확하게 해주어야 한다.



5. NIO

기존 자바 IO에서는 커널 버퍼를 직접 접근하는 소위 Direct Buffer를 핸들링 할 수가 없었습니다. 소켓이나 파일에서 Stream이 들어오면 커널 버퍼에 쓰여지게 되는데, Code상에서 이에 접근 할 수 있는 방법이 없었기 때문입니다. 따라서 JVM이 JVM 내부의 메모리에 불러온 후에야 이 데이터에 접근 할 수 있었는데 “커널에서 JVM내부 메모리로 복사한다”라는 오버헤드가 있었기 때문에 느렸던 거죠. JVM 내부의 메모리라고 하면 int, char변수의나 byte[] 등 자료형 이겠죠. int, char, long 같은 primitive type 은 당연히 JVM내부에서 프로세스별로 할당된 스택에 저장되겠고, 배열은 JVM 내부 힙 메모리에 저장되겠죠. 말로만 하면 어려우니 그림과 함께 설명하도록 하겠습니다.

기존의 Java IO가 디스크에서 파일을 읽을 때의 과정은 다음과 같습니다.

  • Process(JVM)이 file을 읽기 위해 kernel에 명령을 전달
  • Kernel은 시스템 콜(read())을 사용하
  • 디스크 컨트롤러가 물리적 디스크로 부터 파일을 읽어옴
  • DMA를 이용하여 kernel 버퍼로 복사
  • Process(JVM)내부 버퍼로 복사

따라서 다음 과 같은 문제점 이 생길 수 있죠.

  • JVM 내부 버퍼로 복사할 때, CPU가 관여
  • 복사 Buffer 활용 후 GC 대상이 됨
  • 복사가 진행중인 동안 I/O요청한 Thread가 Blocking

위 와 같은 오버헤드가 생길 수 있습니다. 이에 대해 자세히 알아보도록 하겠습니다.


이에 대해 NIO는 커널 버퍼를 직접 활용함으로써 위 과정을 축소시킴.

의미적으로는 NIO Non blocking IO 

네트워크 통신을 예로 들어보면 수천건이 들어와

IO 같은 경우 수천건의 Thread 만들어야돼. 왜 이렇게 하냐면 IO는 Blocking 이 걸리기 때문에 1개씩만 만들게 되있어.


NIO는 Non Blocking 인데 하나의 Thread 가 Channel 관리 (여러개의 요청이 들어올 때 그걸 Queue 나 메모리에 담아놨다가 순서대로 처리를 해주는거야)


NIO 장점 : 수천개의 요청에 대해서 하나의 쓰레드가 담당하니 자원소모가 덜하겠죠. 수천개의 요청에 대해 즉각적인 요청을 해주는 짧은 요청들이 좋겠지. 

NIO 단점 : 하나의 스레드가 스케줄링을 해줘야되니 그 비용이 든다. (그래서 짧은 요청에 대해서 IO 에 비해 처리하는게 좋다.)


IO 장점 : 1:1 Client -server (긴 요청 특히 데이터가 많을 때) 이럴 떄는 IO가 좋다. 배치에 좋다는거지.             



image2







'Language > 자바' 카테고리의 다른 글

객체지향의 개념  (0) 2016.09.26
isInstanceOf vs isAssignable  (0) 2016.09.08
2Phase Commit  (0) 2016.08.17
JVM Locale  (0) 2016.06.23
BCI  (0) 2016.06.09