Java 객체를 null로 설정하면 더 이상 작업이 수행됩니까?


112

나는 오래된 책을 찾아 보던 중 Peter Hagger의 "Practical Java"사본을 발견했습니다. 성능 섹션 null에는 더 이상 필요하지 않을 때 개체 참조를 설정하는 권장 사항이 있습니다.

Java에서 null성능 또는 가비지 콜렉션 효율성 을 향상시키기 위해 오브젝트 참조를 설정 합니까? 그렇다면 어떤 경우에 문제가됩니까? 컨테이너 클래스? 개체 구성? 익명의 내부 클래스?

나는 이것을 코드에서 자주 봅니다. 이것은 이제 쓸모없는 프로그래밍 조언입니까, 아니면 여전히 유용합니까?


2
프로파일 링하십시오. 최신 런타임에서는 성능이나 메모리 공간이 의미있는 증가를 보지 않아야합니다.
Jason Coco

12
@Jason, 프로필? 이 질문에 답할 수있는 충분한 결과 세트를 얻을 수있을만큼 충분히 큰 케이스 세트를 프로파일 링 할 것이라고 가정합니다. 그리고 VM이 gc 및 성능 문제를 감추기에 충분히 최적화 된 경우를 선택하지 않습니다. 그것이 내가 여기서 이것을 묻는 이유입니다. 이것이 문제인 경우를 이해하기 위해.

답변:


73

참조를 무효화하려는시기에 따라 다릅니다.

객체 체인 A-> B-> C가있는 경우 A에 도달 할 수 없으면 A, B 및 C가 모두 가비지 수집 대상이됩니다 (다른 항목이 B 또는 C를 참조하지 않는다고 가정). 예를 들어, 참조 A-> B 또는 B-> C를 명시 적으로 null로 설정할 필요가 없으며 그 어느 때도 필요하지 않았습니다.

그 외에도 실제로는 컬렉션의 개체를 다루기 때문에 대부분의 경우 문제가 실제로 발생하지 않습니다. 일반적으로 적절한 remove () 메서드를 호출하여 목록,지도 등에서 객체를 제거하는 것을 항상 생각해야합니다.

이 케이스 로 사용 널로 설정 레퍼런스에 대한 조언은 구체적으로 하였다 메모리 집약적 오브젝트 범위를 통해 사용 도중에 것을 정지 긴 범위 . 예를 들면 :

{
  BigObject obj = ...
  doSomethingWith(obj);
  obj = null;             <-- explicitly set to null
  doSomethingElse();
}

여기서의 이론적 근거는 obj 가 여전히 범위 내에 있기 때문에 참조를 명시 적으로 무효화하지 않으면 doSomethingElse () 메서드가 완료 될 때까지 가비지 수집 가능 상태가되지 않는다는 것 입니다. 그리고 이것은 아마도 최신 JVM에서 더 이상 유지되지 않는 조언입니다 . JIT 컴파일러가 주어진 로컬 객체 참조가 더 이상 사용되지 않는 지점에서 작업 할 수 있다는 것이 밝혀졌습니다.


2
로컬 변수는 기계에 의해 최적화 될 수 있습니다. 클래스 변수는 할 수 없습니다. 작업자 스레드 (스레드 풀에서)가 요청을 처리 한 후 상태 개체를 해제하지 않아 작업자 스레드가 새 작업을받을 때까지 해당 개체를 메모리에 고정하는 것을 보았습니다 (이전 상태 개체를 새 작업으로 즉시 덮어 씀). 큰 상태 개체와 수백 개의 작업자 스레드 (예 : 큰 http 서버)를 사용하면 엄청난 양의 메모리를 보유하고 복사 할 수 있지만 다시는 사용되지 않습니다.
Brian White

1
그냥 추가해야 할 것 같습니다. 위에 제공 한 조언은 정상적으로 작동하는 라이브러리, 정상적으로 작동하는 VM 등을 가정하여 일반적으로 따라야 할 조언입니다. 예를 들어 객체에 매달리는 스레드 풀 라이브러리가있는 경우 작업이 완료되었습니다. 이것은 스레드 풀 라이브러리의 버그로, 객체 참조를 null로 처리하여 해결할 수 있지만 버그가 적은 스레드 풀 라이브러리를 사용하여 해결할 수 있습니다. 이러한 버그가 일반적인 디자인 원칙을 변경하는지는 잘 모르겠습니다.
Neil Coffey

25

아니, 쓸데없는 조언이 아닙니다. 댕글 링 참조는 여전히 문제입니다. 특히 ArrayList사전 할당 된 배열을 사용하여 확장 가능한 배열 컨테이너 ( 또는 이와 유사한 것)를 구현하는 경우에는 더욱 그렇습니다 . 목록의 "논리적"크기를 초과하는 요소는 제거해야합니다. 그렇지 않으면 해제되지 않습니다.

Effective Java 2nd ed, Item 6 : Eliminate Obsolete Object References를 참조하십시오.


이것은 언어 문제입니까 아니면 VM 구현 문제입니까?
Pablo Santa Cruz

4
"의미 적"문제입니다. 기본적으로 배열을 미리 할당했기 때문에 VM이이를 인식합니다. 컨테이너의 "논리적"크기에 대해 알지 못합니다. 16 요소 배열로 뒷받침되는 크기 10의 ArrayList가 있다고 가정합니다. VM은 항목 10..15가 실제로 사용되지 않는다는 것을 알 수 없습니다. 해당 슬롯에 내용이있는 경우 해제되지 않습니다.
Chris Jester-Young

컨테이너 클래스 외부는 어떻습니까? 내부 개체가 외부 개체에 의해 할당 해제되지 않는 개체 구성에서.
sal

7
@sal 경험의 일반적인 규칙은 참조 할 수 없으면 가비지 수집됩니다. 따라서 외부 개체에 다른 개체에 대한 참조가 포함 된 경우 내부 개체에 다른 참조가 없다고 가정하고 외부 개체의 유일한 참조를 null로 설정하면 고아 참조를 포함하여 전체 개체가 가비지 수집됩니다.
ubermensch

2
당신이 언급 한 동일한 항목 6에서 : 객체 참조를 무효화하는 것은 표준이 아니라 예외 여야합니다.
ACV

10

인스턴스 필드, 배열 요소

개체에 대한 참조가 있으면 가비지 수집 할 수 없습니다. 특히 해당 객체 (및 그 뒤에있는 전체 그래프)가 크면 가비지 수집을 중지하는 참조가 하나만 있고 해당 참조가 더 이상 필요하지 않은 경우 이는 불행한 상황입니다.

병리학 적 사례는 구성에 사용 된 전체 XML DOM 트리에 대한 비 필수적 인스턴스를 유지하는 객체, 등록 해제되지 않은 MBean 또는 전체 클래스 로더가 언로드되는 것을 방지하는 배포되지 않은 웹 응용 프로그램의 객체에 대한 단일 참조입니다. .

따라서 참조 자체를 보유하는 객체가 어쨌든 (또는 그 후에도) 가비지 수집 될 것이라는 확신이없는 경우 더 이상 필요하지 않은 모든 항목을 무효화해야합니다.

범위 변수 :

범위가 끝나기 전에 지역 변수를 null로 설정하여 가비지 수집기에서 회수하고 "지금부터 사용할 수 없음"으로 표시 할 수 있도록 고려중인 경우 대신 더 제한된 범위에 두는 것을 고려해야합니다. .

{
  BigObject obj = ...
  doSomethingWith(obj);
  obj = null;          //   <-- explicitly set to null
  doSomethingElse();
}

된다

{
  {  
     BigObject obj = ...
     doSomethingWith(obj);
  }    //         <-- obj goes out of scope
  doSomethingElse();
}

길고 평평한 범위는 일반적으로 코드의 가독성에도 좋지 않습니다. 그 목적을 위해 사물을 분해하는 사적인 방법을 도입하는 것도 전례가 없습니다.


강력한 참조를 언급하는 경우 이는 사실이지만 모든 참조에 해당하는 것은 아닙니다. Java의 약한 참조는 가비지 수집 될 수 있습니다.
James Drinkard 2013 년

5

메모리 제한 환경 (예 : 휴대폰)에서 이것은 유용 할 수 있습니다. null을 설정하면 objetc는 변수가 범위를 벗어나 gc가 될 때까지 기다릴 필요가 없습니다.

그러나 일상적인 프로그래밍에서는 Chris Jester-Young이 인용 한 것과 같은 특별한 경우를 제외하고는 이것이 규칙이되어서는 안됩니다.


1

첫째, 그것은 당신이 객체를 설정하는 것을 의미하지 않습니다 null. 아래에 설명합니다.

List list1 = new ArrayList();
List list2 = list1;

위의 코드 세그먼트에서 우리는 메모리에 저장된 객체 의 객체 참조 변수 이름 list1을 생성하고 ArrayList있습니다. 그래서 list1그 객체를 언급하는 것은 변수 일뿐입니다. 그리고 코드의 두 번째 줄에 우리의 참조 복사 list1로를 list2. 이제 내가 할 경우 귀하의 질문으로 돌아가십시오.

list1 = null;

즉, list1더 이상 메모리에 저장된 객체를 참조하지 않으므로 참조 할 list2것도 없습니다. 따라서 크기를 확인하면 list2:

list2.size(); //it gives you 0

그래서 여기 에 "객체가 보유한 메모리를 해제하는 것에 대해 걱정할 필요가 없습니다. 프로그램에서 더 이상 사용되지 않고 JVM이 저를 관리 할 것임을 알게되면 그렇게 할 것입니다." 라는 가비지 수집기의 개념이 도착 합니다.

개념이 명확 해 졌으면합니다.


0

그렇게하는 이유 중 하나는 사용되지 않는 개체 참조를 제거하기위한 것입니다. 여기 에서 텍스트를 읽을 수 있습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.