머리말
segmentfault에 대한 질문을 봤습니다. Java에는 완전한 GC 메커니즘이 있으므로 Java에서 메모리 누수가 발생하는지, 메모리 누수 사례를 제공할 수 있습니까? 이 질문 보기는 이 질문에 대한 완전한 답변을 제공합니다.
가비지 수집 메커니즘 소개
프로그램 실행 중에 개체가 생성될 때마다 개체 데이터를 저장하기 위해 일정량의 메모리가 할당됩니다. 계속해서 메모리를 할당하면 프로그램은 조만간 메모리 부족 문제에 직면하게 됩니다. 따라서 모든 언어에는 만료된 개체의 메모리를 해제하여 메모리를 재사용할 수 있도록 하는 메모리 재활용 메커니즘이 있습니다.
메모리 재활용 메커니즘은 구현 역할에 따라 두 가지 유형으로 나눌 수 있습니다. 하나는 프로그래머가 수동으로 메모리를 해제하는 방식(예: C 언어)이고, 다른 하나는 내장된 메모리 재활용 메커니즘입니다. 예를 들어 이 기사에서는 Java 가비지 수집 메커니즘을 소개합니다.
Java의 가비지 수집 메커니즘
프로그램의 런타임 환경에서 Java 가상 머신은 손실된 참조를 재활용하는 시스템 수준의 가비지 수집(GC, Carbage Collection) 스레드를 제공합니다. . 객체가 차지하는 메모리입니다. GC를 이해하기 위한 전제조건은 가비지 컬렉션과 관련된 몇 가지 개념을 이해하는 것입니다. 이러한 개념을 아래에서 하나씩 소개합니다.
jvm 힙 영역의 개체 상태
Java 개체의 인스턴스는 jvm 힙 영역에 저장됩니다. GC 스레드의 경우 이러한 개체에는 세 가지 상태가 있습니다.
1. 도달 가능한 상태: 프로그램에 변수 참조가 있으면 이 객체는 도달 가능한 상태입니다.
2. 부활 가능 상태: 프로그램 내 변수가 이 객체를 참조하지 않으면 객체는 터치 가능 상태에서 부활 가능 상태로 변경됩니다. CG 스레드는 특정 시간에 이 개체의 finalize 메서드를 호출할 준비를 합니다(finalize 메서드는 하위 개체를 상속하거나 다시 작성함). finalize 메서드의 코드는 개체를 터치 가능한 상태로 변환할 수 있습니다. 그렇지 않으면 개체는 불가한 상태로 전환됩니다.
3. Untouchable 상태: 객체가 Untouchable 상태인 경우에만 GC 스레드가 이 객체의 메모리를 회수할 수 있습니다.
객체를 올바르게 해제하려면 GC는 객체 적용, 참조, 참조, 할당 등 각 객체의 실행 상태를 모니터링해야 합니다. GC는 모두 모니터링해야 합니다. 따라서 개체가 위 상태 중 하나에 있든 상관없이 GC는 이를 인식합니다.
위에서 언급한 것처럼 GC 스레드는 특정 시간에 부활 가능한 상태 객체의 finalize 메서드를 실행하게 되는데 언제 실행되나요? 다양한 JVM 구현자가 서로 다른 알고리즘을 사용하여 GC를 관리할 수 있으므로 개발자는 언제든지 GC 스레드에 의한 다양한 작업(객체 상태 감지, 객체 메모리 해제, 객체의 finalize 메서드 호출 포함)의 타이밍을 예측할 수 없습니다. System.gc() 및 Runtime.gc() 함수를 통해 GC 스레드에 가능한 한 빨리 가비지 수집 작업을 수행하도록 상기시킬 수 있지만 GC 스레드가 해당 재활용 작업을 즉시 수행한다는 보장은 없습니다.
메모리 누수
메모리 누수란 잘못된 설계로 인해 더 이상 사용되지 않는 메모리를 프로그램에서 해제하지 못해 자원이 낭비되는 현상을 말합니다. GC는 참조가 손실된 개체가 차지한 메모리를 자동으로 정리합니다. 그러나 프로그래밍 오류로 인해 일부 개체가 항상 참조되면 메모리 누수가 발생합니다.
예를 들어 다음과 같습니다. 스택은 푸시와 팝이라는 두 가지 작업을 사용하여 배열을 사용하여 구현됩니다.
import com.sun.javafx.collections.ElementObservableListDecorator; import com.sun.swing.internal.plaf.metal.resources.metal_sv; import java.beans.ExceptionListener; import java.util.EmptyStackException; /** * Created by peng on 14-9-21. */ public class MyStack { private Object[] elements; private int Increment = 10; private int size = 0; public MyStack(int size) { elements = new Object[size]; } //入栈 public void push(Object o) { capacity(); elements[size++] = o; } //出栈 public Object pop() { if (size == 0) throw new EmptyStackException(); return elements[--size]; } //增加栈的容量 private void capacity() { if (elements.length != size) return; Object[] newArray = new Object[elements.length + Increment]; System.arraycopy(elements, 0, newArray, 0, size); } public static void main(String[] args) { MyStack stack = new MyStack(100); for (int i = 0; i < 100; i++) stack.push(new Integer(i)); for (int i = 0; i < 100; i++) { System.out.println(stack.pop().toString()); } } }
이 프로그램은 사용 가능하며 일반적인 푸시 및 팝 작업을 지원합니다. 그러나 제대로 처리되지 않은 문제가 있습니다. 즉, 스택을 팝할 때 배열의 팝된 요소에 대한 참조가 해제되지 않습니다. 이로 인해 프로그램은 이 개체에 대한 참조를 유지하게 됩니다(이 개체가 참조됩니다). GC는 메모리를 해제하는 것은 물론이고 항상 이 개체에 접근할 수 있다고 생각합니다. 이것은 메모리 누수의 전형적인 경우입니다. 이에 대응하여 수정된 코드는 다음과 같습니다.
//出栈 public Object pop() { if (size == 0) throw new EmptyStackException(); Object o = elements[--size]; elements[size] = null; return o; }
위의 Java 가비지 컬렉션 메커니즘과 메모리 누수에 대한 심층적인 이해에 대한 글은 모두 에디터가 공유한 내용이므로 참고가 되셨으면 좋겠습니다. , 그리고 나는 당신이 PHP 중국어 지원 웹사이트를 더 많이 읽어보기를 바랍니다.
Java 가비지 수집 메커니즘과 메모리 누수 관련 기사에 대한 더 자세한 내용을 보려면 PHP 중국어 웹사이트를 주목하세요!