BufferedWriter, FileWriter와 같이 중간에 버퍼를 두고 사용하는 클래스는 버퍼를 비우기 위한 목적으로 flush() 메서드를 제공한다. 스트림을 통해 데이터를 출력할 때 마지막에 데이터를 누락시키지 않으려면 flush가 꼭 필요하다고 생각했는데 막상 버퍼를 두는 클래스의 예제를 찾아 보면 flush를 사용하지 않는 경우도 많았다. 사용하고 안하고의 차이가 궁금했기에 flush에 대해 조사해봤다. 

 

 

 스트림으로 연결된 두 지점이 있을 때 사용자의 입력이 발생할 경우 즉시 데이터를 출력하지 않고 데이터를 축적하다가 일정량만큼 쌓이면 출력한다. 여기서 데이터를 축적하는 곳이 버퍼이며 한정된 자원인 메모리에 데이터를 무작정 계속 쌓을 수는 없기에 초기의 버퍼의 크기를 정해놓는다. 버퍼의 크기만큼 데이터가 축적되면 내부에서 자동으로 버퍼를 비우도록 되어 있기 때문에 사용 중에 버퍼가 얼마나 찼는지 고려할 필요가 없다. 다만 축적되는 데이터의 총량이 버퍼의 크기로 딱 나누어 떨어질 확률은 거의 없기 때문에 스트림 사용이 끝났을 때 버퍼에 데이터가 남아 있을 확률이 매우 높다. 이러한 경우 flush()를 호출하여 남아 있는 데이터를 방출하는 것이다.

 

 

 flush()를 사용하지 않는 경우는 무엇일까? 바로 close()를 호출하는 것이다. 스트림은 네트워크, 파일, 입출력을 수행할 때 연결하는 데이터를 주고 받는 통로라고 볼 수 있다. 그리고 스트림은 운영체제의 자원을 가져다 쓰는 것이므로 사용하고 나면 반납을 해야 하는데 이 때 close() 메서드를 호출하여 사용한 자원을 해제한다. 버퍼를 사용하는 클래스들도 마찬가지로 스트림을 사용하기 때문에 마지막에 close()를 호출하는데 이 때 내부적으로 flush()를 호출하여 버퍼에 남아 있을지도 모르는 데이터를 강제적으로 방출한다.

 

 

 다시 한 번 버퍼가 비워지는 시점을 정리하면 다음과 같다.

  1. 버퍼의 크기만큼 데이터가 쌓였을 때
  2. 코드에서 flush()를 호출했을 때
  3. 코드에서 close()를 호출했을 때

 

 

 정리하자면 flush를 사용하지 않았던 예제들은 어차피 스트림을 닫으면 close()에서 자동으로 flush()를 호출할 것이니 flush()를 굳이 호출하지 않아도 된다는 생각이었던 것이다.(단순히 까먹었을 수도 있다..)

 

 

 하지만 작업이 끝나는 시점과 close()를 호출하는 시점이 바로 이어지지 않고 중간에 다른 작업을 수행하게 되면 어떻게 될까? 다음 같은 상황을 가정해보자.

BufferedWriter bw = null;

try {
	bw = new BufferedWriter(new OutputStreamWriter(System.out));
	bw.write("A");
	bw.flush();
	bw.write("B");
	System.out.print("C");
} catch(Exception e) {
	e.printStackTrace();
} finally {
	try {
		if(bw != null)
			bw.close();
	} catch(Exception e) {
		e.printStackTrace();
	}
}

 

 

결과는 다음과 같다.

ACB

 

 

 단순히 순서상으로 보면 "ABC" 가 나와야 할 것 같지만 코드를 실행시켜보면 "ACB"가 나온다. 코드를 보면 "A"를 write 한 다음 flush를 해주었으니 버퍼에서 대기하던 "A"가 가장 먼저 출력된다. 그리고 "B"를 write 하였는데 이전에 막 버퍼를 비웠기 때문에 퍼버가 자동으로 비워질 일도 없고 flush(), close()도 호출해주지 않았기 때문에 "B"는 버퍼에 남아 있는 상태로 유지된다. 그리고 "C"를 출력한 뒤 try{..} 지역의 코드가 끝나고 마지막으로 finally에서 스트림을 close() 해준다. close에서 자원을 정리하기 전에 버퍼에 남아 있던 "B"를 출력했기 때문에 결과로 "ACB"가 나온 것이다.

 

 


 

 

 flush()를 호출하지 않아도 close()에서 버퍼를 비워주는 것은 맞다. 하지만 위 예제에서 알 수 있듯이 스트림 사용이 끝나는 시점과 close()를 호출하는 시점이 바로 이어지지 않을 경우 의도한 것과 다른 결과가 나올 수 있다는 것이다. 매번 중간에 다른 작업이 있는가에 대해 신경쓸 바에야 flush()랑 close()를 항상 사용해주는 습관을 들이는 것이 좋겠다.

 

 

+ Recent posts