대빵's Blog

자바에서 메모리 관리와 GC처리 본문

개발관련

자바에서 메모리 관리와 GC처리

bigzero 2017. 4. 3. 15:17

우연히 인터넷에서 아래와 같은 논쟁을 보고나서 전에 정리해놓은것을 꺼내서 remind 할 겸 정리해 보았다.

논쟁은 자바 함수내에서, 즉 지역변수 String a 에서 ....

<1번경우>

for(int i=0,j=xxxArray.length ; i<j ; i++) {

String a = xxxArray[i];

....

}

<2번 경우>

String a;

for(int i=0,j=xxxArray.length ; i<j ; i++) {

a = xxxArray[i];

....

}

뭐 이런식 코드인데 위의 코드에서 String을 for loop 안에 선언하는 경우(1번 경우)와 밖에 선언하는 경우(2번경우)에서 어떤게 더 효율적인지... 아니면 별 차이없는 지 묻는 질문에 많은 사람들이 실로 어이없는 댓글을 달고 본인들이 맞다고 우기는 것을 보고 한번 정리하기로 맘먹었다....(ㅡㅡ;)


<JVM Stack vs Heap, 지역변수선언과 객체의 선언>

자바에서 지역변수(함수내부에 선언하는 변수)는 thread-safe 한 영역에 선언된다.(JVM Stack 이라고 한다.) 이 thread-safe 한 영역은 thread-safe 하므로 당연히 thread 와 생명주기가 같아서 thread 가 죽으면 없어지고(GC) thread 가 안 죽으면 살아있다.

그래서 String a 는 thread-safe 하다. 그런데 for loop 내에서 String a 를 반복적으로 선언하면 이 영역을 중복되게 사용해서 메모리를 낭비하는 꼴이 된다. 즉, JVM Stack을 낭비한다(Heap 영역이 아니다!!!!) . 물론 thread 가 죽으면 GC 대상이 되어 당장 큰 문제가 없을지는 모르지만 만일 해당 함수의 생명주기가 길거나 for loop 데이터가 많은 경우 당연히 문제가 된다.....그래서 2번 경우가 성능상 더 좋은 코드이다..

그런데 웃긴것 그 다음얘기다...논쟁에서 사람들은 Heap의 사용이 잘못되어 메모리가 Full 된다는 둥 머 이런 얘기를 하고 있었던 것이 문제였다....

위의 코드에서 Heap 영역은 무의미하다. 왜냐하면 xxxArray[i] 는 그냥 reference 변수값만을 JVM Stack 내로 할당 시키므로 Heap 영역의 메모리 사용량 증가와는 큰 관계가 없다...(물론 배열은 Heap 영역을 사용하지만 여기서는 배열자체의 데이터가 증가하는 것이 아니지 때문에 Heap 영역의 메모리증가가 발생되지 않는다.)

Heap 영역은 thread-safe 하지 않는 영역으로서 객체선언, 배열/List 등 가변크기객체 등에서 공유해서 사용하는 메모리 영역이다. 자바 멀티쓰레드 프로그래밍에서 thread-safe 와 관련된 문제들은 주로 여기서 발생한다.


<질문의 변경>

아마 질문자는 아래와 같은 코드를 질의하고 싶었을 것이다.

String a;

for( 어쩌구 저쩌구) {

a = new String(i + "번째 String 객체가 생성되었습니다.");

}

이런 코드와

for( 어쩌구 저쩌구) {

String a = new String(i + "번째 String 객체가 생성되었습니다.");

}

개념적으로 위의 둘 중에서 어떤 코드가 올바른 코드인가를 묻고 싶었던거 같은데 질문자가 어이없게 잘못 샘플코드를 작성한 건지 아니면 첨부터 개념을 잘못 잡고 있었던 건지.....

위의 코드에서 첫번째 코드는 지역변수 선언은 한번만 했으므로 JVM Stack 영역은 증가하지 않지만 Heap 을 사용하는 객체선언을 반복적으로 하여 Heap 메모리의 증가를 발생시켜서 의도하지 않은 GC를 발생시킬 것이다.

두번째 코드는 지역변수로 loop , 객체선언도 loop 이므로 JVM Stack 영역의 낭비와 Heap 영역의 낭비를 둘다 발생시킬 것이다.


P.S 1. 위의 첫번째 코드는 당연히 String 을 append 해서 처리해야 한다...인터넷에 샘플 엄청 많으니깐 참고하면 된다...

PS 2. 일단, JVM 의 메모리 관리방법은 JDK 1.8에서 거의 완전히 새로 변경되었으므로(메모리 객체를 자주 쓰는 것/오래된것을 구분하지 않고 그냥 통짜로 처리해 버린다...) 지금쓰는 내용은 1.7 이전 버전에 해당되는 내용이다.