
백준 알고리즘 문제를 풀다가 메모리 초과가 난 문제가 있어서 문득 궁금해졌다.
다른 코드는 모두 똑같고, 이차원 배열을 초기화 하는 코드만 바꿨을 뿐인데 통과가 된 것이다.
초기 코드
// arr과 n 은 모두 static
// arr은 크기가 n x n 인 boolean 타입의 이차원 배열
arr = new boolean[n][n];
바꾼 코드
for(i = 0; i < n; i++){
for(j = 0; j < n; j++){
arr[i][j] = false;
}
}
당연히 첫 번째 코드는 배열을 계속 새로 생성하고, 두 번째 코드는 배열의 값을 바꾸는 것이니 메모리 사용량의 차이가 있는 것이 아닌가? 라는 질문을 할 수도 있지만, 자바에는 Garbage Collector(GC) 라는 것이 존재해서 힙 영역에서 참조되지 않는 객체는 자동으로 제거가 되기 때문에 당연히 메모리 사용량의 차이가 없을 줄 알았다.
따라서 비슷한 경우로 코드를 짜서 메모리 크기 조회를 해봤다.
new 연산자 사용
public static void main(String[] args) {
int n = 30000;
boolean[][] arr = new boolean[n][n];
arr[2][2] = true;
arr = new boolean[n][n];
long usedMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
System.out.println("usedMemory: " + usedMemory + " bytes");
}

이중 for문 사용
public static void main(String[] args) {
int n = 30000;
boolean[][] arr = new boolean[n][n];
arr[2][2] = true;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
arr[i][j] = false;
}
}
long usedMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
System.out.println("usedMemory: " + usedMemory + " bytes");
}

두 경우가 확연히 차이가 나는것을 볼 수 있다.
이러한 이유에 대해 생각해보다가 혹시 GC가 동작하지 않는 것이 아닐까? 라는 의문이 들었다. 따라서 첫 번째 경우에서 System.gc() 를 사용해 강제적으로 GC를 동작시켜 확인해 보았다.
new 연산자 + GC 강제 동작
public static void main(String[] args) {
int n = 30000;
boolean[][] arr = new boolean[n][n];
arr[2][2] = true;
arr = new boolean[n][n];
System.gc();
long usedMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
System.out.println("usedMemory: " + usedMemory + " bytes");
}

예상대로 이중 for문을 사용한 것과 거의 비슷한 수치의 메모리 사용량이 나왔다.
왜 이런 결과가 나왔을까?
GC가 동작하지 않는 원인을 찾기 위해 자바의 메모리 구조 및 GC에 대해 공부를 했다.
그후 내린 결론은 GC에 주기가 있어서 급격한 메모리 사용이 발생하면 Out Of Memory 가 일어날 수 있다는 것이다.
따라서 이러한 문제가 발생한다면
정도의 해결 방법이 있을 것이다.
System.gc()는 단순히 메모리 사용량 확인차 사용한 코드로, 상당히 무거운 작업을 포함하고 있기 때문에 시스템 성능에 매우 큰 영향을 미친다.따라서 절대 실제 서비스용 코드에서 사용해서도 안되며, 사용해야 한다면 이미 메모리면에서 좋은 코드라고 할 수 없을 가능성이 높다.