[Java] 객체 초기화시 메모리 사용량의 차이

김민성·2022년 9월 26일
0

TroubleShooting

목록 보기
2/2
post-thumbnail

객체 초기화시 메모리 사용량 차이


백준 알고리즘 문제를 풀다가 메모리 초과가 난 문제가 있어서 문득 궁금해졌다.

다른 코드는 모두 똑같고, 이차원 배열을 초기화 하는 코드만 바꿨을 뿐인데 통과가 된 것이다.

  • 초기 코드

    // 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) 라는 것이 존재해서 힙 영역에서 참조되지 않는 객체는 자동으로 제거가 되기 때문에 당연히 메모리 사용량의 차이가 없을 줄 알았다.


따라서 비슷한 경우로 코드를 짜서 메모리 크기 조회를 해봤다.

  1. 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");
    }


  1. 이중 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를 동작시켜 확인해 보았다.


  1. 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 가 일어날 수 있다는 것이다.

따라서 이러한 문제가 발생한다면

  1. 불필요한 메모리 사용이 발생하는 코드가 있는지 검토 후, 메모리 사용량 줄이기
  2. GC 튜닝을 이용해 GC의 주기를 조절하기.

정도의 해결 방법이 있을 것이다.

System.gc() 는 단순히 메모리 사용량 확인차 사용한 코드로, 상당히 무거운 작업을 포함하고 있기 때문에 시스템 성능에 매우 큰 영향을 미친다.

따라서 절대 실제 서비스용 코드에서 사용해서도 안되며, 사용해야 한다면 이미 메모리면에서 좋은 코드라고 할 수 없을 가능성이 높다.

0개의 댓글