Java에서 객체를 null에 할당하면 가비지 수집에 영향을 줍니까?


85

nullJava에서 사용되지 않는 객체 참조를 할당하면 가비지 수집 프로세스가 측정 가능한 방식으로 개선됩니까?

Java (및 C #)에 대한 경험을 통해 가상 머신 또는 JIT 컴파일러를 시도하고 능가하는 것이 직관적이지 않은 경우가 많지만 동료가이 방법을 사용하는 것을 보았고 이것이 좋은 방법인지 궁금합니다. 아니면 부두 프로그래밍 미신 중 하나?

답변:


66

일반적으로 아닙니다.

그러나 모든 것들이 그렇듯이 상황에 따라 다릅니다. 요즘 Java의 GC는 매우 훌륭하며 더 이상 도달 할 수없는 즉시 모든 것을 정리해야합니다. 이것은 지역 변수에 대한 메서드를 떠난 직후와 클래스 인스턴스가 더 이상 필드에 대해 참조되지 않을 때입니다.

그렇지 않으면 참조 된 상태로 유지된다는 것을 알고있는 경우에만 명시 적으로 null이 필요합니다. 예를 들어 주변에 보관되는 배열입니다. 더 이상 필요하지 않은 경우 배열의 개별 요소를 null로 지정할 수 있습니다.

예를 들어 ArrayList의 다음 코드는 다음과 같습니다.

public E remove(int index) {
    RangeCheck(index);

    modCount++;
    E oldValue = (E) elementData[index];

    int numMoved = size - index - 1;
    if (numMoved > 0)
         System.arraycopy(elementData, index+1, elementData, index,
             numMoved);
    elementData[--size] = null; // Let gc do its work

    return oldValue;
}

또한 객체를 명시 적으로 null로 설정해도 참조가 남아 있지 않는 한 자연스럽게 범위를 벗어나는 것보다 더 빨리 객체가 수집되지 않습니다.

양자 모두:

void foo() {
   Object o = new Object();
   /// do stuff with o
}

과:

void foo() {
   Object o = new Object();
   /// do stuff with o
   o = null;
}

기능적으로 동일합니다.


7
많은 양의 메모리를 사용하거나 실행하는 데 시간이 오래 걸리는 메서드 내부 또는 오래 실행될 수있는 Swing 애플리케이션의 코드에서 참조를 null로 지정할 수도 있습니다. 짧은 메서드 또는 수명이 짧은 개체에서는 추가로 혼동되는 코드의 가치가 없습니다.
John Gardner

1
그래서 우리가 객체를 무효화하면 가비지 콜렉터가 해당 객체를 즉시 수집합니까 ??
Muhammad Babar 2013 년

1
아니요. 가비지 수집기는 참조 변수가 가리키는 개체가 없는지 확인하기 위해 주기적으로 실행됩니다. null로 설정 한 다음 호출 System.gc()하면 제거됩니다 (해당 개체를 가리키는 다른 참조가없는 경우).
JavaTechnical 2014

2
@JavaTechnical 내가 알고 있듯이 System.gc () 가비지 수집이 시작된다는 보장은 없습니다.
Jack

12

내 경험상, 사람들은 종종 필요가 아니라 편집증 때문에 참고 문헌을 무효화합니다. 다음은 빠른 지침입니다.

  1. 객체 A를 참조가 B 객체 경우 더 이상이 참조를 필요로하지 객체 A가 가비지 컬렉션에 적합하지 않습니다 당신은 명시 적으로 필드를 null로해야한다. 둘러싸고있는 객체가 어쨌든 가비지 수집을받는 경우 필드를 null로 만들 필요가 없습니다. dispose () 메서드에서 필드를 제거하는 것은 거의 항상 쓸모가 없습니다.

  2. 메서드에서 생성 된 개체 참조를 null 아웃 할 필요가 없습니다. 메소드가 종료되면 자동으로 지워집니다. 이 규칙의 예외는 매우 긴 메서드 또는 대규모 루프에서 실행 중이고 메서드가 끝나기 전에 일부 참조를 지워야하는 경우입니다. 다시 말하지만, 이러한 경우는 극히 드뭅니다.

대부분의 경우 참조를 무효화 할 필요가 없습니다. 가비지 수집기를 능가하는 것은 쓸모가 없습니다. 비효율적이고 읽을 수없는 코드로 끝날 것입니다.


11

좋은 기사는 오늘의 코딩 공포 입니다.

GC의 작업 방식은 포인터가없는 객체를 찾는 것입니다. 검색 영역은 힙 / 스택 및 기타 공간입니다. 따라서 변수를 null로 설정하면 실제 개체는 이제 누구도 가리 키지 않으므로 GC가 될 수 있습니다.

그러나 GC가 정확한 순간에 실행되지 않을 수 있으므로 실제로는 아무것도 구매하지 않을 수 있습니다. 그러나 방법이 상당히 길다면 (실행 시간 측면에서) GC가 해당 객체를 수집 할 가능성이 높아 지므로 그만한 가치가 있습니다.

이 문제는 코드 최적화로도 복잡 할 수 있습니다. 변수를 null로 설정 한 후 변수를 사용하지 않는 경우 값을 null로 설정하는 줄을 제거하는 것이 안전한 최적화입니다 (실행할 명령이 하나 적음). 따라서 실제로 개선되지 않을 수도 있습니다.

요약하면 도움이 될 수 있지만 결정적이지는 않습니다 .



ref를 null로 설정하는 줄을 제거하는 안전한 최적화에 대한 좋은 점입니다.
asgs apr

8

적어도 자바에서는 부두 프로그래밍이 아닙니다. 다음과 같은 것을 사용하여 Java에서 객체를 만들 때

Foo bar = new Foo();

두 가지 작업을 수행합니다. 첫째, 객체에 대한 참조를 생성하고 둘째, Foo 객체 자체를 생성합니다. 해당 참조 또는 다른 참조가 존재하는 한 특정 객체는 gc'd 될 수 없습니다. 그러나 해당 참조에 null을 할당하면 ...

bar = null ;

다른 객체에 대한 참조가 없다고 가정하면 다음 번 가비지 수집기가 지나갈 때 gc에서 해제되고 사용할 수 있습니다.


12
그러나 범위를 벗어나면 추가 코드 줄없이 동일한 작업을 수행 할 수 있습니다. 메서드에 로컬이면 필요하지 않습니다. 메소드에서 나가면 객체는 GC를 사용할 수 있습니다.
duffymo

2
좋은. 이제 팝 퀴즈의 경우 충분하지 않은 코드 예제를 구성하십시오. 힌트 : 존재합니다.
Charlie Martin

7

때에 따라 다르지.

일반적으로 개체에 대한 참조를 짧게 유지하면 수집 속도가 빨라집니다.

메서드를 실행하는 데 2 ​​초가 걸리고 메서드 실행 1 초 후에 더 이상 개체가 필요하지 않은 경우 해당 참조를 지우는 것이 좋습니다. GC가 1 초 후에도 객체를 참조하면 다음 번에 1 분 정도 후에 확인할 수 있습니다.

어쨌든, 기본적으로 모든 참조를 null로 설정하는 것은 저에게 조기 최적화이며 메모리 소모를 현저하게 감소시키는 특정 드문 경우가 아니면 아무도 수행해서는 안됩니다.


6

변수가 범위를 벗어나도록하는 대신 명시 적으로 null에 대한 참조를 설정하는 것은 보유한 개체가 매우 크지 않는 한 가비지 수집기에 도움이되지 않습니다. 작업을 마치는 즉시 null로 설정하는 것이 좋습니다.

일반적으로 참조를 null로 설정하는 것은이 개체가 완전히 완료되었으며 더 이상 염려하지 않아야하는 코드의 READER를 의미합니다.

추가 중괄호 세트를 넣어 더 좁은 범위를 도입하여 유사한 효과를 얻을 수 있습니다.

{
  int l;
  {  // <- here
    String bigThing = ....;
    l = bigThing.length();
  }  // <- and here
}

이렇게하면 중첩 된 중괄호를 떠난 직후에 bigThing이 가비지 수집 될 수 있습니다.


좋은 대안. 이 명시 적으로 null로 변수를 설정 피한다 린트는 일부의 IDE가없는 사용이 널 (null) 할당에 대한 표시 경고.
jk7

5
public class JavaMemory {
    private final int dataSize = (int) (Runtime.getRuntime().maxMemory() * 0.6);

    public void f() {
        {
            byte[] data = new byte[dataSize];
            //data = null;
        }

        byte[] data2 = new byte[dataSize];
    }

    public static void main(String[] args) {

        JavaMemory jmp = new JavaMemory();
        jmp.f();

    }

}

위의 프로그램은 OutOfMemoryError. 주석을 제거 data = null;하면 OutOfMemoryError문제가 해결됩니다. 항상 사용하지 않는 변수를 null로 설정하는 것이 좋습니다.


즉, imo는 짚맨 논쟁의 세계로의 견고한 진출입니다. 변수를 null로 설정하면 특정 경우에 문제가 해결되는 상황을 만들 수 있기 때문에 (그리고 그렇게했다고 확신하지는 않습니다) 모든 경우에 null로 설정하는 것이 좋은 생각이라는 것을 의미하지는 않습니다. 더 이상 사용하지 않는 모든 변수를 null로 설정하는 코드를 추가하면 곧 범위를 벗어나더라도 코드가 부풀어 오르고 코드의 유지 관리가 어려워집니다.
RHSeeger

변수 데이터가 범위를 벗어난 순간에 byte [] 배열 가비지가 수집되지 않는 이유는 무엇입니까?
Grav

2
@Grav : 범위를 벗어난다고해서 가비지 수집기가 로컬 개체를 수집한다는 보장은 없습니다. 그러나이를 null로 설정하면 OutOfMemory 예외가 발생하지 않습니다. OutOfMemory 예외를 throw하기 전에 GC가 실행되고 개체가 null로 설정되어 있으면 수집 할 수 있기 때문입니다.
스테파노 스 T.


4

한 번은 화상 회의 응용 프로그램을 작업하고 있었는데 더 이상 개체가 필요하지 않게 되 자마자 null 참조에 시간을 할애했을 때 성능면에서 엄청난 차이를 발견했습니다. 이것은 2003-2004 년이었고 GC가 그 이후로 더 똑똑해 졌다고 상상할 수 있습니다. 제 경우에는 매초마다 수백 개의 개체가오고가는 개체가 있었기 때문에 GC가 주기적으로 시작될 때 발견했습니다. 그러나 null 객체를 가리킨 후 GC가 내 응용 프로그램을 일시 중지하는 것을 중지했습니다.

그래서 그것은 당신이하는 일에 달려 있습니다 ...


2

예.

"The Pragmatic Programmer"p.292에서 :

참조를 NULL로 설정하면 개체에 대한 포인터 수를 1 개 줄일 수 있습니다. 그러면 가비지 수집기가 제거 할 수 있습니다.


나는 이것이 참조 카운트 알고있을 때만 적용 가능하다고 생각합니다. 사용 중입니까?
Nrj

3
이 조언은 현대 가비지 수집기에서는 쓸모가 없습니다. 참조 카운팅 GC에는 순환 참조와 같은 중요한 문제가 있습니다.
Lawrence Dol

마크 앤 스윕 GC에서도 마찬가지입니다. 아마도 다른 모든 것에서. 참조가 하나 적다고해서 참조가 계산되는 것은 아닙니다. tweakt의 대답이 이것을 설명합니다. NULL로 설정하는 것보다 범위를 줄이는 것이 좋습니다.

1

OP가 다음과 같은 것을 참조한다고 가정합니다.

private void Blah()
{
    MyObj a;
    MyObj b;

    try {
        a = new MyObj();
        b = new MyObj;

        // do real work
    } finally {
        a = null;
        b = null;
    }
}

이 경우 VM이 범위를 벗어나는 즉시 GC로 표시하지 않습니까?

또는 다른 관점에서 항목을 명시 적으로 null로 설정하면 범위를 벗어난 경우 이전에 GC를 받게 될까요? 그렇다면 VM은 어쨌든 메모리가 필요하지 않을 때 객체를 GC하는 데 시간을 소비 할 수 있으며, 이는 실제로 GC가 더 일찍 수행 될 것이기 때문에 실제로 CPU 사용 성능을 저하시킬 수 있습니다.


GC가 실행되는 시간은 결정적이지 않습니다. 객체를 설정 null하여 GC 동작에 전혀 영향을 주지 않는다고 생각합니다 .
harto

0

" 에 따라 다릅니다 "

Java에 대해서는 잘 모르지만 .net (C #, VB.net ...)에서는 더 이상 객체가 필요하지 않을 때 일반적으로 null을 할당 할 필요가 없습니다.

그러나 "일반적으로 필요하지 않음"에 유의하십시오.

코드를 분석함으로써 .net 컴파일러는 변수의 수명을 잘 평가하여 객체가 더 이상 사용되지 않는시기를 정확하게 알려줍니다. 따라서 obj = null을 작성하면 실제로 obj가 여전히 사용되고있는 것처럼 보일 수 있습니다.이 경우 null을 할당하는 것은 비생산적입니다.

실제로 null을 할당하는 데 도움이 될 수있는 몇 가지 경우가 있습니다. 한 가지 예는 오랫동안 실행되는 거대한 코드 나 다른 스레드 또는 일부 루프에서 실행되는 메서드가 있다는 것입니다. 이러한 경우 GC가 더 이상 사용되지 않음을 쉽게 알 수 있도록 null을 할당하는 것이 도움이 될 수 있습니다.

이것에 대한 엄격하고 빠른 규칙은 없습니다. 위의 코드에 null 할당을 배치하고 프로파일 러를 실행하여 어떤 식 으로든 도움이되는지 확인하십시오. 대부분의 경우 혜택을 보지 못할 수 있습니다.

최적화하려는 .net 코드 인 경우 Dispose 및 Finalize 메서드를 잘 관리하는 것이 실제로 null을 신경 쓰는 것보다 더 유익하다는 것을 경험했습니다.

주제에 대한 일부 참조 :

http://blogs.msdn.com/csharpfaq/archive/2004/03/26/97229.aspx

http://weblogs.asp.net/pwilson/archive/2004/02/20/77422.aspx


0

참조를 무효화하는 것이 약간 더 효율적이더라도 이러한 추악한 무효화로 코드를 보완해야하는 추악한 가치가 있습니까? 그것들은 혼란스럽고 그것들을 포함하는 의도 코드를 모호하게합니다.

가비지 수집기를 능가하는 것보다 더 나은 최적화 후보가없는 드문 코드베이스입니다 (아직도이를 능가하는 데 성공한 개발자는 드물다). 당신의 노력은 대신 다른 곳에서 쓰는 것이 더 좋을 것입니다. 그 잔인한 Xml 파서를 버리거나 계산을 캐시 할 기회를 찾으십시오. 이러한 최적화는 정량화가 더 쉬우 며 코드베이스를 잡음으로 더럽힐 필요가 없습니다.


0

나중에 프로그램을 실행할 때 일부 데이터 멤버의 값은 프로그램 외부에서 볼 수있는 출력을 컴퓨터 화하는 데 사용됩니다. 다른 것들은 프로그램에 대한 미래의 (그리고 예측 불가능한) 입력에 따라 사용되거나 사용되지 않을 수 있습니다. 다른 데이터 멤버는 사용되지 않을 수 있습니다. 사용하지 않는 데이터에 할당 된 메모리를 포함한 모든 리소스가 낭비됩니다. GC (가비지 수집기)의 역할은 낭비되는 메모리를 제거하는 것입니다. GC가 필요한 것을 제거하는 것은 재앙이 될 수 있으므로 사용 된 알고리즘은 엄격한 최소값 이상을 유지하면서 보수적 일 수 있습니다. 실제로 필요하지 않은 일부 항목을 유지하면서 속도를 향상시키기 위해 휴리스틱 최적화를 사용할 수 있습니다. GC가 사용할 수있는 많은 잠재적 알고리즘이 있습니다. 따라서 프로그램을 변경할 수 있습니다. 프로그램의 정확성에 영향을 미치지 않는 경우에도 GC의 작동에 영향을 주어 동일한 작업을 더 빠르게 실행하거나 사용하지 않는 항목을 더 빨리 식별 할 수 있습니다. 그래서 이런 종류의 변경, unusdd 객체 참조를null, 이론적 으로 항상 부두가 아닙니다.

부두인가? 이를 수행하는 Java 라이브러리 코드의 일부가 있습니다. 해당 코드의 작성자는 일반 프로그래머보다 훨씬 뛰어나며 가비지 수집기 구현의 세부 사항을 알고있는 프로그래머를 알고 있거나 협력합니다. 따라서 때때로 이점 있음을 시사 합니다.


0

최적화가 있다고 말했듯이, 즉 JVM은 변수가 마지막으로 사용 된 장소를 알고 있으며이 변수가 참조하는 객체는이 마지막 지점 (현재 범위에서 여전히 실행 중) 바로 직후에 GC 될 수 있습니다. 따라서 대부분의 경우 참조를 무효화하면 GC에 도움이되지 않습니다.

그러나 (또는 "부유 쓰레기") 문제 ( "족벌주의"를 피하기 위해 유용 할 수 있습니다 자세한 내용은 여기를 읽 거나 동영상 감상 ). 힙이 Old 세대와 Young 세대로 나뉘고 적용되는 GC 메커니즘이 다르기 때문에 문제가 발생합니다. Minor GC (빠르고 젊은 세대를 청소하는 데 자주 발생 함) 및 Major Gc (Old 세대를 청소하는 데 더 오래 일시 중지됨). "Nepotism"은 Young Gen의 쓰레기가 이미 Old Gen에 보관 된 쓰레기에 의해 참조되는 경우 수집하는 것을 허용하지 않습니다.

승격 된 노드는 GC가 문제를 해결할 때까지 모든 후속 노드가 승격되기 때문에 이는 '병리 적'입니다.

네 포티 즘을 피하려면 제거해야하는 객체에서 참조를 무효화하는 것이 좋습니다. 이 기술이 JDK 클래스에 적용된 것을 볼 수 있습니다 : LinkedListLinkedHashMap

private E unlinkFirst(Node<E> f) {
    final E element = f.item;
    final Node<E> next = f.next;
    f.item = null;
    f.next = null; // help GC
    // ...
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.