더 이상 필요 하지않을 때 close 메서드를 사용해서 명시적으로 닫아야 하는 리소스가 있습니다. 코틀린/JVM에서 사용하는 자바 표준 라이브러리에는 이런 리소스들이 많습니다.
- InputStream과 OutputStream
- java.sql.Connection
- java.io.Reader(FileReader, BufferedReader,CSSParser)
- java.new.Socket과 java.utill.Scanner
등이 있습니다. 이러한 리소스들은 AutoCloseable을 상속받는 Closeable 인터페이스를 구현하고 있습니다.
이러한 리소스는 최종적으로 리소스에 대한 참조(reference)가 없어질 때, 가비지 컬렉터가 처리합니다.
하지만 굉장히 느리며 그동안 리소스를 유지하는 비용이 많이 들어갑니다. 따라서 더이상 필요하지 않다면 명시적으로 close 매서드를 호출해 주는 것이 좋습니다. 전통적으로 이러한 리소스는 try-finally 블록을 사용해서 처리합니다.
fun countCharactersInFile(path: String): Int{
val reader = BufferedReader(FileReader(path))
try {
return reader.lineSequence().sumBy { it.length }
} finally {
reader.close()
}
}
하지만 이러한 코드는 좋지 않습니다. 리소스를 닫을 때 이러한 예외를 따로 처리하지 않기 때문입니다. 또한 try 블록과 finally 블록 내부에서 오류가 발생한다면 둘 중 하나만 전파 됩니다. 둘다 전파되게 구현하면 좋지만 그러면 굉장히 길고 복잡해 집니다. 그래도 많이 사용되는 일반적인 구현이므로 표준 라이브러리에는 use라는 이름의 함수로 포함되어 있습니다. use 함수를 사용해서 앞의 코드를 적절하게 변경하면, 다음과 같습니다. 이러한 코드는 Closeable객체에 사용할 수 있습니다.
fun countCharactersInFile(path: String): Int{
val reader = BufferedReader(FileReader(path))
reader.use {
return reader.lineSequence().sumBy { it.length }
}
}
람다 매개변수 리시버가 전달되는 형태도 있습니다.
fun countCharactersInFile(path: String): Int{
BufferedReader(FileReader(path)).use { reader ->
return reader.lineSequence().sumBy { it.length } }
}
파일을 리소스로 사용하는 경우도 많고, 파일을 한 줄씩 읽어 들이는 경우도 많으므로, 코틀린 표준 라이브러리는 파일을 한 줄씩 처리할 때 활용할 수 있는 useLines함수도 제공 합니다.
fun countCharactersInFile(path: String): Int {
File(path).useLines { lines ->
return lines.sumBy { it.length }
}
}
이렇게 처리하면 메모리 파일에 파일의 내용을 한 줄씩만 유지하므로, 대용량 파일도 적절하게 처리할 수 있습니다. 다만 파일의 줄을 한 번만 사용할 수 있다는 단점이 있습니다. 파일의 특징 줄을 두 번 이상 반복 처리하려면, 파일을 두번 이상 열어야 합니다. 앞의 코드는 다음과 같이 간단하게 작성할 수도 있습니다.
fun countCharactersInFile(path: String): Int =
File(path).useLines { lines ->
return lines.sumBy { it.length }
}
시퀸스 활용해 파일 처리 법 ------------------------
정리
use를 사용하면 Closeble/AutoCloseable을 구현한 객체를 쉽고 안전하게 처리할 수 있습니다. 또한 파일을 처리할 때는 파일을 한 줄씩 읽어 들이는 useLines를 사용하는 것이 좋습니다.
가비지 컬렉터
여러가지 코드를 작성해 선언하다 보면 메모리 공간에 쌓이게 됩니다. 간혹 비워주어야 할 메모리 공간을 놓칠 수 있습니다. 이러다 보면 memory leak, 메모리 누수가 발생 합니다.
memory leak이란 메모리를 비워주지 못해 메모리가 꽉차고 이 때문에 프로그램이 종료되거나 컴퓨터가 뻗는 현상을 말합니다 이를 방지하기 위해 프로그래머가 코딩 중 일일이 명시적으로 메모리 공간을 비워 주어야 하는 언어가 있습니다 c 언어 에서는 malloc()함수를 이용해 메모리 공간을 할당하여 사용합니다. 이렇게 할당한 메모리는 다시 해제를 해주어야 하는데 c언어에서는 free()함수를 이용해 할당 받은 메모리를 해제할 수 있습니다
반면에 java를 비롯한 기타 언어에서는 가비지 컬렉터가 이를 대신해 줍니다. 메모리를 비워야 할 일을 프로그래머가 직접 하지 않고 가비지 컬렉터가 알아서 해줍니다
Mark and Sweep : 필요 없는 가비지를 버리는 방식
가비지 컬렉션의 루트부터 시작해서 루트가 참조하는 모든 오브젝트, 그 오브젝트가 참조하는 또 다른 오브젝트 등을 모두 검사하며 참조 관계를 마크합니다. 그리고 마크가 안된 것들을 다 버리는 방식
Reference Counting
한 요소가 다른 요소에게 몇 번이나 참조가 되었는지를 세어서 만약 참조가 0번이라면 더 이상 필요 없는 요소로 판단하여 버리는 방식을 말합니다. 따라서 서로가 서로를 참조하는 방식으로 코드를 짜게 되면 reference counting은 0 이 되지 않기 때문에 가비지 컬렉터가 이를 버릴 수 없습니다. 그래서 이런 코드 작성은 지양 해야 합니다
---
가비지 컬렉터가 전부 잡지를 못하기에 지속적으로 신경을 써야 합니다 하지만 명시적으로 메모리 해제를 위해 System.gc()를 호출하는것은 시스템 성능에 영향을 끼치기에 쓰지 말아야 합니다