System.gc ()를 호출하는 것이 왜 나쁜 습관입니까?


326

Java로 객체강제 사용하지 않는 방법 (1.5GB HashMap을 지우는 방법)에 대한 질문에 대답 한 후 수동으로 호출하는 것은 나쁜 습관이라고 들었지만 주석은 완전히 설득력이 없었습니다. 또한, 아무도 내 대답을 공감하거나 감히 내려 놓는 것처럼 보이지 않았습니다.System.gc()System.gc()

나는 거기에 나쁜 습관이라고 들었지만 가비지 컬렉터 실행은 더 이상 체계적으로 세상을 멈추지 않으며 JVM에서 힌트로만 효과적으로 사용할 수 있다고 들었습니다. 손실에.

JVM이 메모리를 회수해야 할 때 일반적으로 사용자보다 더 잘 알고 있음을 이해합니다. 또한 몇 킬로바이트의 데이터에 대한 걱정은 어리 석다는 것을 이해합니다. 또한 메가 바이트의 데이터조차도 몇 년 전의 데이터가 아니라는 것을 이해합니다. 그러나 여전히 1.5 기가 바이트? 그리고 메모리에 1.5GB의 데이터가 걸려 있다는 것을 알고 있습니다. 어둠 속에서 촬영하는 것과는 다릅니다. 가 System.gc()체계적으로 불량이거나 괜찮이되는 몇 가지 포인트가있다?

따라서 문제는 실제로 두 배입니다.

  • 전화하는 것은 왜 나쁜 일 System.gc()입니까? 실제로 특정 구현에서 JVM에 대한 힌트일까요? 아니면 항상 전체 수집주기입니까? 세상을 멈추지 않고 작업을 수행 할 수있는 가비지 수집기 구현이 실제로 있습니까? 사람들이 내 답변 에 대한 의견을 제시 한 다양한 주장에 대해 약간의 설명을 부탁드립니다 .
  • 임계 값은 어디에 있습니까? 에 전화하는 것은 좋은 생각 이 아니System.gc() 거나 허용되는 시간이 있습니까? 그렇다면 그 시간은 무엇입니까?

4
System.gc ()를 호출하기에 좋은 시간은 이미 긴로드 프로세스 인 것을 할 때입니다. 예를 들어 게임을하고 있는데로드 프로세스가 끝날 때 게임이 새로운 레벨을로드 할 때 System.gc ()를 호출 할 계획입니다. 사용자는 이미 약간 기다리고 있으며 추가 성능 향상은 그만한 가치가 있습니다. 구성 화면에이 동작을 비활성화하는 옵션을 배치하겠습니다.
Ricket

41
Bozho : 질문의 첫 단어에 주목하십시오. 왜 전화하지 않는 것이 가장 좋은 방법입니까? 만트라를 반복한다고해서 저주받은 것은 설명되지 않습니다.
저의 올바른 의견에 동의하십시오

1
또 다른 경우는 java-monitor.com/forum/showthread.php?t=188 에서 설명되었습니다 . 여기서 System.gc ()를 호출하는 것이 좋지 않은 방법을 설명합니다.
Shirishkumar Bari

답변:


243

모든 사람이 항상 피해야 System.gc()하는 이유는 코드가 근본적으로 손상 되었다는 꽤 좋은 지표 이기 때문입니다 . 정확성을 위해 의존하는 코드는 확실히 손상되었습니다. 성능에 의존하는 것은 대부분 파손되었을 가능성이 있습니다.

어떤 종류의 가비지 수집기가 실행되고 있는지 모릅니다. 확실히 당신이 주장하는 것처럼 "세계를 막지" 않는 것이 있지만, 일부 JVM은 그렇게 똑똑하지 않거나 여러 가지 이유로 (아마도 전화 중입니까?) 그렇게하지 않습니다. 당신은 그것이 무엇을할지 모른다.

또한 아무것도하지 않을 수도 있습니다. JVM은 요청을 완전히 무시할 수 있습니다.

"무엇을할지 모르겠다", "도움이 될지 모른다", "어쨌든 전화 할 필요가 없다"의 조합은 사람들이 일반적으로 당신은 그것을 호출해서는 안됩니다. "이것을 사용해야하는지 물어봐야한다면 안된다"고 생각합니다.


다른 스레드의 몇 가지 문제를 해결하도록 편집 하십시오.

링크 된 스레드를 읽은 후에 몇 가지 더 지적하고 싶습니다. 먼저 누군가 호출 gc()하면 시스템에 메모리를 반환 할 수 있다고 제안했습니다 . Java 힙 자체는 Java 할당과 독립적으로 커집니다.

마찬가지로, JVM은 메모리 (수십 메가 바이트)를 보유하고 필요에 따라 힙을 증가시킵니다. Java 객체를 해제하더라도 시스템에 해당 메모리를 반환 할 필요는 없습니다. 향후 Java 할당에 사용하기 위해 할당 된 메모리를 보유 할 수 있습니다.

System.gc()아무것도 할 수 없음을 나타내려면 다음을보십시오.

http://bugs.sun.com/view_bug.do?bug_id=6668279

특히 -XX : DisableExplicitGC VM 옵션이 있습니다.


1
GC가 실행되는 방법이 코드의 정확성에 영향을 미치는 이상한 Rube Goldberg-esque 설정을 구성 할 수 있습니다. 어쩌면 이상한 스레딩 상호 작용을 숨기거나 아마도 최종 프로그램이 프로그램 실행에 큰 영향을 줄 수 있습니다. 나는 그것이 가능하다는 것을 완전히 확신하지는 못하지만 가능할 수도 있으므로 언급 할 것이라고 생각했습니다.
Steven Schlansker 2

2
@zneak 당신은 예를 들어 파이널 라이저 (기본적으로 깨진 코드)에 중요한 코드를 넣었을 수도 있습니다
Martin

3
System.gc()유용하고 필요할 수 있는 몇 가지 코너 사례가 있다고 덧붙이고 싶습니다 . 예를 들어 Windows의 UI 응용 프로그램에서 Window 를 최소화 하기 전에 System.gc ()를 호출하면 (특히, 일정 시간 동안 최소화 된 상태로 프로세스의 일부가 바뀌는 경우) Window의 복원 프로세스 속도를 크게 높일 수 있습니다. 디스크).
Joachim Sauer

2
@AndrewJanke WeakReference잡고 싶은 객체에 대해 s를 사용하는 코드가 시작, 가비지 수집에서 올바르지 않다고 말하고 싶습니다 . C ++에서도 동일한 문제가 발생합니다 std::weak_ptr(물론 객체 소멸이 일반적으로 마무리처럼 지연되지 않기 때문에 Java 버전보다 C ++ 버전에서 문제가 있음을 알 수 있습니다).
JAB

2
@rebeccah 그것은 버그입니다. 그렇습니다. 나는 그것을 분명히 깨뜨 렸습니다. System.gc()그것을 고치는 사실은 좋은 코딩 방법이 아닌 해결 방법입니다.
Steven Schlansker

149

호출 system.gc() 아무것도 수 없으며 가비지 콜렉터를 "실행해야하는"코드가 손상 되었다고 이미 설명했습니다 .

그러나 전화하는 것이 좋지 않은 실용적인 이유 System.gc()는 비효율적이라는 것입니다. 그리고 최악의 경우, 그것은 매우 비효율적입니다 ! 설명하겠습니다.

일반적인 GC 알고리즘은 힙에있는 모든 비가 비지 개체를 통과하고 방문하지 않은 개체는 가비지 여야 함을 유추하여 가비지를 식별합니다. 이를 통해 가비지 콜렉션의 전체 작업을 실제 데이터 양에 비례하는 한 부분과 가비지 양에 비례하는 다른 부분으로 구성 할 수 있습니다. 즉 work = (live * W1 + garbage * W2).

이제 단일 스레드 애플리케이션에서 다음을 수행한다고 가정하십시오.

System.gc(); System.gc();

첫 번째 부름은 (우리가 예측 한) 일 (live * W1 + garbage * W2)을하고 미처리 쓰레기를 제거 할 것입니다.

두 번째 부름은 (live* W1 + 0 * W2)효과가 있으며 아무것도 되찾지 못할 것 입니다. 다시 말해 우리는 (live * W1)일을 해왔고 전혀 아무것도 달성하지 못했습니다 .

가비지 단위를 수집하는 데 필요한 작업량으로 수집기의 효율성을 모델링 할 수 있습니다. 즉 efficiency = (live * W1 + garbage * W2) / garbage. 따라서 GC를 최대한 효율적으로 만들려면 GC를 실행할 때 의 가치 를 극대화 해야합니다 garbage. 즉, 힙이 가득 찰 때까지 기다리십시오. 또한 힙을 최대한 크게 만드십시오. 그러나 이는 별도의 주제입니다.

응용 프로그램이 (호출하여 System.gc()) 방해하지 않으면 GC는 힙이 가득 찰 때까지 기다렸다가 가비지 1 을 효율적으로 수집 합니다. 그러나 응용 프로그램에서 GC를 강제로 실행하면 힙이 가득 차지 않을 가능성이 있으며 결과적으로 가비지가 비효율적으로 수집됩니다. 그리고 응용 프로그램이 GC를 더 자주 강제할수록 GC가 더 비효율적으로됩니다.

참고 : 위의 설명은 일반적인 최신 GC가 힙을 "공간"으로 분할하고 GC가 힙을 동적으로 확장 할 수 있으며 응용 프로그램의 비가 비지 객체 작업 세트가 다양 할 수 있다는 사실을 강조합니다. 그럼에도 불구하고, 동일한 기본 원칙이 모든 실제 가비지 수집기 2에 걸쳐 전체적으로 적용됩니다 . GC를 강제로 실행하는 것은 비효율적입니다.


1- "처리량"수집기가 작동하는 방식입니다. CMS 및 G1과 같은 동시 수집기는 서로 다른 기준을 사용하여 가비지 수집기를 시작할시기를 결정합니다.

2-참조 카운트를 독점적으로 사용하는 메모리 관리자도 제외하고 있지만 현재 Java 구현에서는 그 방법을 사용하지 않습니다 ...


45
+1 좋은 설명입니다. 그러나이 추론은 처리량에 관심이있는 경우에만 적용됩니다. 특정 지점에서 잠재 성을 최적화하려면 GC를 적용하는 것이 좋습니다. 예를 들어 게임에서 (가설 적으로 말하면) 레벨 동안 지연을 피하고 싶지만 레벨로드 동안 지연에 대해서는 신경 쓰지 않습니다. 그런 다음 레벨로드 후 GC를 강제하는 것이 합리적입니다. 전반적인 처리량을 줄이지 만 최적화하려는 것은 아닙니다.
sleske

2
@sleske-당신이 말하는 것은 사실입니다. 그러나 레벨간에 GC를 실행하는 것은 반창고 솔루션입니다. 레벨이 길어도 레벨 동안 GC를 실행해야하는 경우 대기 시간 문제를 해결하지 못합니다. 더 나은 방법은 플랫폼에서 지원하는 경우 동시 (일시 중지) 가비지 수집기를 사용하는 것입니다.
Stephen C

"가비지 수집기가 실행되어야 함"의 의미가 무엇인지 확실하지 않습니다. 응용 프로그램이 피해야 할 조건은 다음과 같습니다. 결코 충족 될 수없는 할당 실패 및 높은 GC 오버 헤드. 임의로 System.gc ()를 호출하면 종종 높은 GC 오버 헤드가 발생합니다.
Kirk

@Kirk- "무작위로 System.gc ()를 호출하면 종종 높은 GC 오버 헤드가 발생합니다." . 나도 알아 내 대답을 읽고 이해 한 사람이라면 누구나 그러할 것입니다.
Stephen C

@Kirk- "의미하는 바가 확실하지 않다 ..." -프로그램의 "올바른"동작이 특정 시간에 실행중인 GC에 의존하는 프로그램을 의미합니다. 예를 들어, 종료자를 실행하거나 WeakReferences 또는 SoftReferences를 중단하기 위해. 이것을하는 것은 정말 나쁜 생각입니다 ...하지만 이것이 내가 말하는 것입니다. Steven Schlansker의 답변도 참조하십시오.
Stephen C

47

많은 사람들이 당신에게 이것을하지 말라고 말하고있는 것 같습니다. 동의하지 않습니다. 레벨로드와 같은 큰로드 프로세스 후에 다음을 믿는 경우 :

  1. 도달 할 수없고 gc'ed되지 않은 많은 오브젝트가 있습니다. 과
  2. 이 시점에서 사용자가 약간의 둔화를 견딜 수 있다고 생각합니다.

System.gc ()를 호출해도 아무런 해가 없습니다. 나는 그것을 c / c ++ inline키워드 처럼 본다 . 그것은 개발자에게 시간 / 성능이 평소만큼 중요하지 않으며 일부는 메모리를 회수하는 데 사용될 수 있다고 결정한 gc에 대한 힌트 일뿐입니다.

아무것도하지 않는 것에 대한 조언은 맞습니다. 작동에 의존하지 말고 지금은 수집하기에 적당한 시간이라는 힌트를주는 것이 좋습니다. 사용자가 프로그램과 적극적으로 상호 작용할 때 (게임 레벨과 같이) 코드에서 중요하지 않은 (로드 화면) 시점에서 시간을 낭비하고 싶습니다.

한 번은 수집 을 강요 할 때가 있습니다. 특정 객체 누수 (기본 코드 또는 대규모의 복잡한 콜백 상호 작용)를 찾으려고 할 때 Matlab에서 볼 수있는 UI 구성 요소 는 절대 사용해서는 안됩니다 . 생산 코드에서.


3
멤 누출을 분석하는 동안 GC의 경우 +1 힙 사용량 (Runtime.freeMemory () 등)에 대한 정보는 GC를 강제 실행 한 후에 만 ​​의미가 있습니다.
sleske

4
System.gc ()를 호출해도 아무런 해가 없습니다.stop the world 접근 이 필요할 수 있으며 , 이것이 일어날 경우 이는 실제 피해입니다
bestsss

7
입니다 명시 적으로 가비지 컬렉터를 호출에 해를 끼치. 잘못된 시간에 GC를 호출하면 CPU주기가 낭비됩니다. 적절한 시간이 언제인지 결정할 수있는 충분한 정보가 프로그래머에게는 없지만 JVM에는 있습니다
Stephen C

Jetty는 시작 후 System.gc ()를 두 번 호출하지만 차이가 거의 없습니다.
Kirk

Jetty 개발자들은 버그가 있습니다. 차이를 정량화하기 어려운 경우에도 차이가 발생합니다.
Stephen C

31

사람들은 왜 사용하지 말아야 하는지를 잘 설명해 왔기 때문에 사용해야 할 몇 가지 상황을 알려 드리겠습니다.

(다음 주석은 CMS 콜렉터로 Linux에서 실행되는 Hotspot에 적용 System.gc()되며 실제로는 항상 전체 가비지 콜렉션을 호출 한다고 확신 합니다.)

  1. 응용 프로그램을 처음 시작한 후에는 메모리 사용 상태가 끔찍할 수 있습니다. tenured 세대의 절반은 쓰레기로 가득 차있을 수 있습니다. 즉, 첫 CMS에 훨씬 가깝습니다. 중요한 응용 프로그램에서는 System.gc ()를 호출하여 힙을 라이브 데이터의 시작 상태로 "재설정"하는 것은 좋지 않습니다.

  2. # 1과 동일한 행을 따라 힙 사용량을 면밀히 모니터링하는 경우 기준 메모리 사용량을 정확하게 읽습니다. 응용 프로그램의 가동 시간 중 처음 2 분이 모두 초기화 인 경우 전체 gc를 강제로 설정하지 않으면 데이터가 엉망이됩니다.

  3. tenured generation이 실행되는 동안 아무것도 홍보하지 않도록 설계된 응용 프로그램이있을 수 있습니다. 그러나 아마도 거대 세대가 아닌 세대로 자동으로 이동하려면 거추장스럽지 않은 데이터를 미리 초기화해야 할 수도 있습니다. 모든 것이 설정된 후 System.gc ()를 호출하지 않으면 데이터가 승격 될 때까지 새로운 세대에 데이터가 저장 될 수 있습니다. 갑작스럽게 슈퍼 듀퍼 낮은 지연 시간, 낮은 GC 애플리케이션은 정상적인 작업 중에 이러한 객체를 승격시키는 데있어 엄청난 지연 (물론 말하면) 지연 페널티에 부딪칩니다.

  4. 메모리 누수가 있는지 확인하기 위해 프로덕션 응용 프로그램에서 System.gc 호출을 사용하는 것이 유용한 경우가 있습니다. X 시점의 라이브 데이터 세트가 Y 시점의 라이브 데이터 세트에 대해 특정 비율로 존재해야한다는 것을 알고 있다면 System.gc ()에 X 시간과 Y 시간을 호출하고 메모리 사용량을 비교하는 것이 유용 할 수 있습니다 .


1
대부분의 세대 가비지 수집기의 경우, 새로운 세대의 객체는 특정 수의 구성 가능한 가비지 수집에서 살아남 아야하므로 System.gc()한 번만 호출 하면 객체를 승격 시켜도 아무런 효과가 없습니다. 그리고 여러분은 System.gc()여덟 번 연속 으로 전화를 걸기를 원치 않으며 지금은 프로모션이 완료되었으며 이후 프로모션 비용이 절감되어 여러 개의 전체 GC 비용이 합리적임을기도합니다. GC 알고리즘에 따라 메모리를 이전 세대에 재 할당하거나 동시에 복사하기 때문에 많은 객체를 홍보하는 데 실제 비용이 들지 않을 수도 있습니다.
Holger

11

이것은 매우 귀찮은 질문이며, 언어의 유용성에도 불구하고 많은 사람들이 Java에 반대하는 데 기여한다고 생각합니다.

"System.gc"가 무엇이든 할 수 없다는 사실은 믿을 수 없을 정도로 어렵고 "공포, 불확실성, 의심"느낌을 언어에 쉽게 부를 수 있습니다.

대부분의 경우 중요한 이벤트가 발생하기 전에 의도적으로 발생하는 메모리 스파이크를 처리하는 것이 좋습니다. 이로 인해 사용자는 프로그램이 잘못 설계되었거나 응답하지 않는다고 생각하게됩니다.

가비지 수집을 제어 할 수있는 능력은 매우 훌륭한 교육 도구가 될 것입니다. 따라서 가비지 수집의 작동 방식과 프로그램이 기본 동작 및 제어 된 동작을 악용하는 방법을 사람들의 이해도를 향상시킬 수 있습니다.

이 스레드의 인수를 검토하겠습니다.

  1. 비효율적입니다.

종종 프로그램이 아무 것도하지 않을 수 있으며 프로그램 방식으로 인해 아무것도하지 않는 것을 알고 있습니다. 예를 들어, 큰 대기 메시지 상자를 사용하여 일종의 오래 기다릴 수 있으며 마지막에는 쓰레기를 수집하는 호출을 추가하여 실행 시간이 실제로는 오래 기다리지 만 더 중요한 작업 중에 gc가 작동하지 않도록합니다.

  1. 항상 나쁜 습관이며 깨진 코드를 나타냅니다.

동의하지 않습니다. 가비지 수집기가 무엇인지는 중요하지 않습니다. 쓰레기를 추적하고 청소하는 것이 직업입니다.

사용량이 덜 중요한 시간에 gc를 호출하면 수명이 특정 코드에 의존하지만 가비지를 수집하기로 결정할 때 실행 가능성을 줄입니다.

물론, 원하는 방식이나 예상대로 작동하지 않을 수도 있지만 전화를 걸 때 아무 일도 일어나지 않으며 사용자는 속도 저하 / 다운 타임을 용인 할 수 있습니다. System.gc가 작동하면 훌륭합니다! 그렇지 않은 경우 적어도 시도했습니다. 가비지 수집기가 수동으로 호출 할 때 가비지 수집기가 동작하는 방식에 예상치 못한 일을하는 고유의 부작용이 없다면 단순히 단점은 없으며, 이는 자체적으로 불신을 야기합니다.

  1. 일반적인 사용 사례는 아닙니다.

안정적으로 달성 할 수없는 유스 케이스이지만 시스템이 그러한 방식으로 설계된 경우 일 수 있습니다. 교통 신호등을 만들고 교통 신호등의 일부 / 모든 버튼이 아무것도하지 않도록하는 것과 같습니다. 버튼이 시작되는 이유에 대한 의문을 제기하고, 자바 스크립트에는 가비지 수집 기능이 없으므로 그것을 면밀히 조사하지 마십시오.

  1. 사양에 따르면 System.gc ()는 GC를 실행해야한다는 힌트이며 VM은이를 무시할 수 있습니다.

"힌트"란 무엇입니까? "무시"란 무엇입니까? 컴퓨터는 단순히 힌트를 얻거나 무언가를 무시할 수 없으며, 시스템의 의도에 따라 동적 일 수있는 엄격한 행동 경로가 있습니다. 적절한 답변은 가비지 수집기가 실제로 구현 수준에서 수행하는 작업을 포함하며, 이로 인해 요청시 수집이 수행되지 않습니다. 이 기능은 단순히 nop입니까? 어떤 조건이 충족되어야합니까? 이 조건들은 무엇입니까?

Java GC는 종종 당신이 믿지 않는 괴물처럼 보입니다. 언제오고 갈 것인지, 그것이 무엇을할지, 어떻게할지 모릅니다. 가비지 콜렉션이 명령별로 작동하는 방식에 대해 더 잘 알고있는 일부 전문가를 상상할 수 있지만 대다수는 단순히 "그냥 작동"하기를 희망하며, 불투명 한 알고리즘을 신뢰하여 작업을 수행하는 것은 실망 스럽습니다.

무언가를 읽거나 무언가를 배우는 것과 실제로 그것을 구현하는 것, 시스템 간의 차이점을 보는 것, 그리고 소스 코드를 보지 않고도 그것을 이용할 수있는 것 사이에는 큰 차이가 있습니다. 이것은 자신감과 숙달 / 이해 / 통제 감을 만듭니다.

요약하자면, "이 기능은 아무 것도하지 않을 수도 있으며, 언제 어떤 일을하고 언제 무엇을하지 않는지, 어떻게하지 않을 것인지, 왜하지 않을 것인지, 왜 그렇게하지 않을 것인지에 대해 자세히 설명하지 않을 것입니다." "그 뒤에 의도가 합리적 일지라도 그것을 시도하는 것은 단순히 철학에 위배된다는 것을 암시한다".

Java GC가 작동하는 방식대로 작동하는 것이 좋을 수도 있고 그렇지 않을 수도 있지만 이해하기 위해서는 GC가 수행 할 수있는 일에 대한 포괄적 인 개요를 얻기 위해 어떤 방향으로 가야하는지 실제로 이해하기가 어렵습니다. 언어의 목적은 행동을 철학적으로 제어하는 ​​것이기 때문에 (특히 프로그래머, 특히 초보자는 특정 시스템 / 언어의 행동으로 인해 존재하는 위기에 빠지기 쉽기 때문에) 언어를 불신하는 것은 너무 쉬운 일이 아닙니다. 용인 할 수 있고 (그렇지 않으면 언어를 사용하지 않을 것입니다.) 제어 할 수없는 알려진 이유없이 제어 할 수없는 것들이 본질적으로 해 롭습니다.


9

GC 효율성은 여러 가지 휴리스틱에 의존합니다. 예를 들어 일반적인 휴리스틱은 개체에 대한 쓰기 액세스가 일반적으로 오래 전에 생성되지 않은 개체에서 발생한다는 것입니다. 또 다른 하나는 많은 객체가 수명이 매우 짧다는 것입니다 (일부 객체는 오랫동안 사용되지만 생성 후 몇 마이크로 초 후에 폐기 됨).

전화 System.gc()는 GC를 차는 것과 같습니다. "모든 신중하게 조정 된 매개 변수, 스마트 조직, 개체를 할당하고 관리하는 모든 노력이 순조롭게 진행됩니다. 모든 작업을 중단하고 처음부터 시작하십시오." 그것은 수 있습니다 성능을 개선하지만, 그것은 단지 대부분의 시간을 저하 성능을.

System.gc()안정적 으로 사용하려면 (*) GC가 세부적으로 어떻게 작동하는지 알아야합니다. 다른 벤더의 JVM 또는 동일한 벤더의 다음 버전 또는 동일한 JVM이지만 약간 다른 명령 행 옵션을 사용하는 경우 이러한 세부 사항은 상당히 변경되는 경향이 있습니다. 따라서 모든 매개 변수를 제어하는 ​​특정 문제를 해결하지 않는 한 좋은 아이디어는 아닙니다. 따라서 "나쁜 습관"이라는 개념은 금지되어 있지 않으며 방법은 존재하지만 거의 보상을하지 않습니다.

(*) 여기서 효율성에 대해 이야기하고 있습니다. 올바른 Java 프로그램을 중단System.gc() 하지 않습니다 . JVM이 다른 방법으로는 얻을 수 없었던 추가 메모리를 활용하지는 않습니다.를 던지기 전에 JVM은 최후의 수단 일지라도 작업을 수행합니다 .OutOfMemoryErrorSystem.gc()


1
System.gc ()가 OutOfMemoryError를 막지 못한다는 언급에 +1. 어떤 사람들은 이것을 믿습니다.
sleske

1
실제로 는 소프트 참조 처리로 인해 OutOfMemoryError를 방지 할 수 있습니다 . 마지막 GC 실행 후에 생성 된 SoftReferences는 내가 알고있는 구현에서 수집되지 않습니다. 그러나 이것은 구현 세부 사항이며 언제든지 변경 될 수 있으며 일종의 버그이며 의존해서는 안됩니다.
maaartinus

8

때때로 ( 종종 그렇지는 않습니다! ) 런타임, 현재 및 미래의 메모리 사용량에 대해 더 많이 알고 있습니다. 이것은 자주 발생하지 않으며 일반 페이지가 제공되는 동안 웹 응용 프로그램에서 절대 주장하지 않습니다.

몇 년 전 저는 보고서 생성기에서 작업했습니다.

  • 단일 스레드가 있었다
  • 대기열에서 "보고서 요청"을 읽습니다.
  • 데이터베이스에서 보고서에 필요한 데이터를로드했습니다.
  • 보고서를 생성하여 이메일로 발송했습니다.
  • 미해결 요청이 없을 때까지 자면서 영원히 반복되었습니다.
  • 보고서간에 데이터를 재사용하지 않았으며 현금화도하지 않았습니다.

첫째, 실시간이 아니고 사용자가 보고서를 기다릴 것으로 예상했기 때문에 GC 실행은 문제가되지 않았지만 요청 된 것보다 빠른 속도로 보고서를 작성해야했습니다.

위의 프로세스 개요를 보면 분명합니다.

  • 다음 요청이 아직 처리되지 않았기 때문에 보고서가 이메일로 전송 된 직후에 실제 개체가 거의 없다는 것을 알고 있습니다.
  • 가비지 콜렉션주기를 실행하는 비용은 라이브 오브젝트 수에 따라 다르며 가비지 양은 GC 실행 비용에 거의 영향을 미치지 않습니다.
  • 대기열이 비어 있으면 GC를 실행하는 것이 더 좋습니다.

따라서 요청 큐가 비어있을 때마다 GC 실행을 수행하는 동안 가치가 있습니다. 이것에 대한 단점은 없었습니다.

GC 실행에 적합한시기라는 것을 알고 있으므로 각 보고서를 이메일로 보낸 후에 GC 실행을 수행하는 것이 좋습니다. 그러나 컴퓨터에 충분한 램이 있으면 GC 실행을 지연시켜 더 나은 결과를 얻을 수 있습니다.

이 동작은 각 보고서마다 보고서 보호 속도크게 향상 시킨 후 강제 GC를 사용하도록 설정 한 일부 고객을 위해 설치 기반별로 구성되었습니다 . (이것은 서버의 메모리가 부족하고 많은 다른 프로세스를 실행하기 때문에 GC가 페이징을 줄 이도록 충분한 시간을 가졌다 고 생각합니다.)

작업 대기열이 비어있을 때마다 강제 GC 실행이 도움이되지 않는 설치를 감지하지 못했습니다.

그러나 위의 내용은 일반적인 경우가 아닙니다.


3

어색한 코드를 작성했지만 Eclipse 및 netbeans IDE에서 휴지통 아이콘을 클릭하는 것이 '좋은 습관'이라는 것을 알게되었습니다.


1
그럴 수도 있습니다. 그러나 Eclipse 또는 NetBeans가 System.gc()주기적으로 호출되도록 프로그래밍 된 경우 , 동작이 성가 시게됩니다.
Stephen C

2

첫째, 사양과 현실에는 차이가 있습니다. 사양에 따르면 System.gc ()는 GC를 실행해야한다는 힌트이며 VM은이를 무시할 수 있습니다. 실제로 VM은 절대 System.gc () 호출을 무시 하지 않습니다 .

GC를 호출하면 호출에 사소한 오버 헤드가 제공되며 임의의 특정 시점 에서이 작업을 수행하면 노력에 대한 보상이 없을 것입니다. 반면에 자연스럽게 트리거되는 컬렉션은 통화 비용을 회수 할 가능성이 높습니다. System.gc ()를 호출 할 수있는 것보다 GC를 실행해야한다는 정보가 있으면 이점을 볼 수 있습니다. 그러나 System.gc ()를 호출 해야하는지 여부와 시간을 이해하기에 충분한 정보가 없을 가능성이 있기 때문에 몇 가지 경우에만 발생합니다.

IDE에있는 쓰레기통을 치면 여기에 나열된 예가 있습니다. 회의에 참석하지 않으시면 회의에 참석하십시오. 오버 헤드는 영향을 미치지 않으며 다시 돌아올 때 힙이 정리 될 수 있습니다. 생산 시스템에서이 작업을 수행하고 수집을 자주 요청하면 분쇄 작업이 중단됩니다! RMI에서와 같은 통화는 성능에 지장을 줄 수 있습니다.


3
"실제로 VM은 System.gc () 호출을 무시하지 않습니다." -틀렸어 -XX : -DisableExplicitGC에 대해 읽으십시오.
Stephen C

1

예. System.gc ()를 호출한다고해서 실행이 보장되는 것은 아니며 JVM에 대한 요청이므로 무시 될 수 있습니다. 문서에서 :

gc 메소드를 호출하면 Java Virtual Machine이 사용하지 않는 객체를 재활용하기 위해 노력을 소비 함을 제안합니다.

자동 메모리 관리는 일반적으로 gc를 할 때보 다 더 잘 알고 있기 때문에 호출하는 것은 거의 항상 나쁜 생각입니다. 사용 가능한 메모리의 내부 풀이 부족하거나 OS가 일부 메모리를 전달하도록 요청하면 그렇게됩니다.

당신이 경우에 System.gc ()를 호출하는 것이 허용 될 수 알고 그것을하는 데 도움이. 즉, 배포 플랫폼 에서 두 시나리오의 동작을 철저히 테스트하고 측정했으며 도움이 될 수 있습니다. gc는 쉽게 예측할 수 없다는 점을 명심하십시오-그것은 한 달리기에 도움이되고 다른 달리기에 상처를 줄 수 있습니다.


<stroke> 그러나 Javadoc에서도 _ 제어가 메소드 호출에서 리턴되면 가상 머신은 버린 모든 오브젝트를 재활용하기 위해 최선을 다했습니다. > 오해의 소지가 있다는 버그 보고서가 있습니다. 어느 쪽이 더 잘 아는가?
zneak

1
잘못된 시간에 수집을 수행하면 속도가 크게 느려질 수 있습니다. 당신이주는 힌트는 아마 나쁜 것입니다. "최선의 노력"의견에 대해서는 JConsole과 같은 도구를 사용해보십시오. 때때로 "GC 수행"버튼을 클릭해도 아무 반응이 없습니다
tom

동의하지 않지만 죄송합니다. OpenJDK에서 System.gc () 및이를 기반으로하는 모든 항목 (예 : HP)은 항상 가비지 콜렉션주기를 발생시킵니다. 사실 또한 IBM의 J9 구현의 진정한 보인다
커크

@Kirk-잘못된 : google, -XX : -DisableExplicitGC에 대해 읽습니다.
Stephen C

0

필자의 경험에 따르면 System.gc ()를 사용하는 것은 플랫폼 별 최적화 형식입니다 (여기서 "플랫폼"은 하드웨어 아키텍처, OS, JVM 버전 및 사용 가능한 RAM과 같은 더 많은 런타임 매개 변수의 조합입니다). 특정 플랫폼에서는 대략 예측할 수 있지만 플랫폼마다 상당히 다를 수 있습니다.

예 . System.gc ()가 성능을 개선 (인식) 하는 상황 이 있습니다 . 예를 들어 앱의 일부 부분에서는 지연이 허용되지만 다른 부분에서는 지연이 허용되지 않는 경우가 있습니다 (위에서 인용 한 게임 예제, 레벨이 아닌 레벨의 시작 부분에서 GC가 발생하도록하려는 경우).

그러나 그것이 도움이되는지 아프게 할 것인지 (또는 아무것도하지 않을 것인지)는 플랫폼에 따라 크게 좌우됩니다 (위에 정의 된 바와 같이).

따라서 마지막 리조트 플랫폼 별 최적화로 유효하다고 생각합니다 (즉, 다른 성능 최적화가 충분하지 않은 경우). 그러나 특정 벤치 마크없이 도움이 될 수 있다고 생각하기 때문에 전화해서는 안됩니다.


0
  1. new 연산자를 사용하여 객체가 동적으로 할당되므로
    이러한 객체가 어떻게 소멸되고
    나중에 재 할당하기 위해 메모리가 해제 되는지 궁금 할 것 입니다.

  2. C ++와 같은 일부 언어에서는 동적으로 할당 된 객체를 delete 연산자를 사용하여 수동으로 해제해야합니다.

  3. Java는 다른 접근법을 취합니다. 자동으로 할당 해제를 처리합니다.
  4. 이를 수행하는 기술을 가비지 수집이라고합니다. 객체에 대한 참조가 존재하지 않으면 해당 객체가 더 이상 필요하지 않은 것으로 간주되고 객체가 차지하는 메모리를 회수 할 수 있습니다. C ++ 에서처럼 객체를 명시 적으로 제거 할 필요는 없습니다.
  5. 가비지 콜렉션은 프로그램 실행 중 산발적으로 만 발생합니다.
  6. 더 이상 사용되지 않는 하나 이상의 개체가 존재하기 때문에 발생하지 않습니다.
  7. 또한 다른 Java 런타임 구현은 가비지 수집에 대한 다양한 접근 방식을 취하지 만 대부분의 경우 프로그램을 작성하는 동안 그것에 대해 생각할 필요가 없습니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.