자바의 레퍼런스 변수와 가비지 컬렉터

Haiseong Jeong·2022년 9월 28일
3
post-thumbnail

자바는 포인터가 없다.

C언어와 C++는 포인터 문법을 제공한다. 포인터 문법은 아주 강력한 기능을 제공하지만 문법적 실수를 만들 확률이 높아진다. 자바 개발자들은 이 문법으로 얻는 득보다 실이 크다고 생각했나보다. 자바는 포인터 문법을 제공하지 않는다.

포인터 대신 레퍼런스

포인터 문법이 없는 대신 자바는 레퍼런스라는 문법을 제공한다. 제공이라기 보다는 강제로 써야한다. 자바에서 클래스를 만들어봤다면 이미 이 문법을 사용하고 있는것이다.

클래스와 인스턴스

직사각형을 뜻하는 Rectangle 클래스를 만든다.

public class Rectangle {
	int width;
	int height;
    
	public int getArea() {
    	return width*height;
	}
}

getArea() 메서드를 이용해 사각형의 면적을 구할수 있다. 그러나 함수를 바로 호출하면 에러가 발생한다. 클래스와 클래스의 메서드를 이용하기 위해서는 반드시 메모리에 생성해줘야 하기 때문이다. 이렇게 메모리에 클래스가 실제로 만들어진것을 인스턴스라고 부른다.

getArea(); // error!
Rectangle rec = new Rectangle(); // 인스턴스 생성
rec.getArea(); // 출력

이렇게 인스턴스가 만들어져야 비로소 클래스의 메서드를 실행할 수 있다. 여기서 처음 자바를 접하는 사람들이 착각하는 것이 있다. rec을 Rectangle형 변수라고 생각하는 것이다.

아니 포인터 없다며

rec은 Rectangle형 변수가 아닌 Rectangle 인스턴스를 가리키는 레퍼런스 변수이다. 가리키는?? 이거 포인터에서 듣던 설명 아닌가? 아니 포인터 없다며 모든 인스턴스는 러퍼런스 변수를 통해서만 사용이 가능하다. 다른말로 클래스를 정의하고 사용하기 위해서는 레퍼런스 변수를 사용해야 한다.

다음 코드를 보자.

Rectangle rec1 = new Rectangle(); // 인스턴스 생성
rec1.width = 10;
rec1.height = 5;
Rectangle rec2 = rec1; // rec2에 rec1대입
rec2.width = 30; // rec2.width 변경
rec1.getArea(); // rec1 면적 출력 : 150

rec2 = rec1에서 rec2는 rec1이 가리키는 메모리 주소를 받는다. 그래서 rec2.width를 30으로 바꾸고 rec1의 면적을 출력했을때 결과가 150 (30 * 5)로 나온다.

다음은 클래스 배열을 만드는 과정을 보자.

Rectangle [] rec = new Rectangle [10];
rec[0].width = 3; // error!

문제가 없어보이는데 에러가 발생한다. 에러 내용을 보면 NullPointerException 라고 한다. 레퍼런스 변수가 가리키고 있는 주소가 없다는 뜻이다.

Rectangle [] rec = new Rectangle [10];
for(int i = 0; i < 10; i++)
	rec[i] = new Rectangle();
rec[0].width = 3; // good!

자바에서는 이렇게 각 인덱스마다 인스턴스를 생성해줘야한다.

해제는 어떻게 하나...

C언어에 익숙한 사람이면 위 코드가 불편할 것이다. C언어에서는 malloc 함수로 할당받은 동적 메모리를 free를 이용해서 해제 해줘야한다. 그래야 memory leak이 발생하지 않는다. 자바는 어떻게 할당을 해제해 줄까? 정답부터 말하면 신경 안써도 된다.

가비지 컬렉터만 믿으라고

자바는 가비지 컬렉터(garbage collector)를 지원한다. 이는 동적으로 할당받은 메모리를 자동으로 해제해준다. 가비지 컬렉터는 어떻게 메모리가 더이상 사용되지 않는다는것을 판단해서 해제해주는걸까? 레퍼런스 변수가 설명해준다. 메모리 공간을 가리키는 레퍼런스 변수 하나당 하나씩 카운트가 늘어난다. 레퍼런스 변수가 삭제되면 다시 카운트는 줄어든다. 그러다가 더이상 메모리를 가리키는 레퍼런스 변수가 없으면 카운트는 0이 될것이다. 가비지 컬렉터는 이렇게 카운트가 0이된 메모리를 다시 회수해 오는것이다.

잘 돌아가던 게임이 갑자기 느려질때가 있어요

안드로이드폰 게임은 자바로 프로그래밍 되어있는 경우가 많다. 가끔 3d게임을 하다가 갑자기 느려지는 경험을 해봤을 것이다. 사양은 충분한데 왜 랙이 걸리지.. 라고 생각하는 순간이 가비지 컬렉터가 일을 하고 있는 시간이다. 위에서는 마치 레퍼런스 카운트가 0이 되는순간 가비지 컬렉터가 바로 메모리를 회수하는것처럼 설명을 했지만 사실 그렇게 작동하지 않는다. 여러 메모리들이 모이면 가비지 컬렉터가 이들을 한번에 회수하기 시작한다. 이때 랙이 걸리는 것이다. 가비지 컬렉터가 언제 일을할지는 아무도 모른다. 조절 할 수가 없다... 그래서 여러가지 인스턴스를 처음에 배열로 만들어 놓고 게임이 끝날때까지 변수를 삭제하지 않는방법으로 갑자기 발생하는 랙을 없애는 방법도 사용된다고 한다. 카운트가 0으로 내려가지 않으므로 가비지 컬렉터가 일을 하지 않는다..

profile
나는 개발자다. 5000만큼 코딩한다.

0개의 댓글