Java 쓰레기 수거 이해

7065 단어 Java쓰레기 수거
프로그램이 대상, 그룹 등 인용 유형의 실체를 만들 때 시스템은 메모리에 이 대상에 메모리를 분배하고 대상은 이 메모리에 저장한다. 이 메모리가 더 이상 인용 변수에 인용되지 않을 때 이 메모리는 쓰레기가 되어 쓰레기 회수 메커니즘이 회수되기를 기다린다.쓰레기 수거 메커니즘은 세 가지 특징을 가지고 있다.
쓰레기 회수 메커니즘은 메모리 중의 대상만 회수하고 물리적 자원(예를 들어 데이터베이스 연결, 열린 파일 자원 등)을 회수하지 않으며, 특정한 대상을 만드는 방식 이외의 방식으로 이 이미지에 분배된 메모리를 회수하지 않는다. (예를 들어 대상이 로컬 방법에서malloc를 호출하는 방식)
프로그램은 쓰레기 회수 운행을 정확하게 제어할 수 없으며, 쓰레기 회수 진행만 건의할 수 있으며, 건의하는 방식은 두 가지 시스템이 있다.gc() 및 Runtime.getRuntime().gc()
쓰레기가 어떤 대상을 회수하기 전에 그의finalize () 방법을 먼저 사용하지만 쓰레기 회수 시기와 일치하고finalize () 방법을 사용하는 시기도 확실하지 않다.
위의 세 가지 특징에 대해 다음과 같은 세 가지 문제가 있습니다.
1. 수동으로 정리 작업을 하고 대상을 만드는 방식을 제외한 방식으로 분배된 메모리와 다른 물리적 자원을 방출해야 한다.또한 만료된 객체 참조를 제거하지 않으면 OOM이 발생할 수 있습니다.
수동 청소는 일반적으로try에 사용됩니다.finally...이런 코드 구조.
예는 다음과 같습니다.

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class ManualClear {

 public static void main(String[] args) {
  FileInputStream fileInputStream = null;
  try {
   fileInputStream = new FileInputStream("./src/ManualClear.java");
  } catch (FileNotFoundException e) {
   System.out.println(e.getMessage());
   e.printStackTrace();
   return;
  }

  try {
   byte[] bbuf = new byte[1024];
   int hasRead = 0;
   try {
    while ((hasRead = fileInputStream.read(bbuf)) > 0) {
     System.out.println(new String(bbuf, 0, hasRead));
    }
   } catch (IOException e) {
    e.printStackTrace();
   }
  } finally {
   try {
    fileInputStream.close();
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
 }

}
기한이 지난 대상의 인용에 있어서 발생하는 OOM은 보통 세 가지 흔한 상황이 있는데 이 세 가지 상황은 통상적으로 발견하기 어렵고 짧은 시간 내에 운행해도 아무런 문제가 없지만 시간이 지나면 누설된 대상이 증가하면 결국 프로그램의 붕괴를 일으킬 수 있다.
클래스 스스로 메모리를 관리할 때, 메모리 유출을 경계해야 한다
예는 다음과 같습니다.

import java.util.Arrays;
import java.util.EmptyStackException;

class Stack{
 private Object[] elements;
 private int size;
 private static final int DEFAULT_INITAL_CAPACITY = 16;
 
 public Stack() {
  elements = new Object[DEFAULT_INITAL_CAPACITY];
 }
 
 public void push(Object e){
  ensureCapacity();
  elements[size++] = e;
 }
 
 public Object pop() {
  if (size == 0) {
   throw new EmptyStackException();
  }
  
  return elements[--size];
 }
 
 private void ensureCapacity() {
  if (elements.length == size) {
   elements = Arrays.copyOf(elements, 2 * size + 1);
  }
 }
}

public class StackDemo {
 
 public static void main(String[] args) {
  Stack stack = new Stack();
  
  for (int i = 0; i < 10000; i++) {
   stack.push(new Object());
  }
  
  for(int i = 0; i < 10000; i++) {
   stack.pop();
  }
 }

}
메모리 유출은 프로그램의 다른 대상이 더 이상 인용하지 않더라도 Stack 클래스의elements[] 수조는 여전히 이 대상의 인용을 저장하고 있기 때문에 이 대상들은 쓰레기 회수에 회수되지 않기 때문에 클래스가 메모리를 관리해야 할 때 내부 유지보수의 이 기한이 지난 인용이 제때에 인용이 해제되었는지 경계해야 한다.
elements[size] = null;됐어.
캐시는 메모리 유출을 경계해야 한다
이러한 상황은 일반적으로 대상을 캐시에 넣으면 오랫동안 사용하지 않으면 잊어버리기 쉽다. 보통 WakeHashMap으로 캐시를 대표할 수 있으며, 캐시의 항목이 만료되면 자동으로 삭제될 수 있다.버퍼의 만료 항목을 제거하기 위해 백그라운드 루틴을 정기적으로 실행할 수도 있습니다.
감청기나 리셋된 등록은 표시할 수 있는 등록 취소가 가장 좋다.
2、finalize () 를 수동으로 호출하지 마세요. 쓰레기 수거기에 호출합니다.
3. 피nalize () 방법을 사용하지 않고 판단 종결 조건으로 대상에 적당히 정리되지 않은 부분을 발견하지 않는 경우를 피한다.안전망으로 수동으로 청소를 잊어버리고 호출을 잊어버린 상황에서 시스템 자원을 청소하는 데 사용되며, 뒤로 청소를 미루는 것은 영원히 청소하지 않는 것이 강하다. 또한 자원을 잊어버린 정보를 동시에 기록하면 뒤에서 오류를 발견하고 잊어버린 코드를 제때에 수정할 수 있다.대상의 로컬 방법을 방출하는 것은 그리 중요한 시스템 자원이 아니다.
finalize () 방법은 실행 시간과 실행 여부를 정확하게 확보할 수 없기 때문에 관건적인 자원을 방출하지 않는 것이 좋으나 위에서 말한 세 가지 상황에 사용할 수 있습니다.이 중 첫 번째 상황은 다음과 같습니다.

class Book {
 boolean checkout = false;
 public Book(boolean checkout) {
  this.checkout = checkout;
 }
 
 public void checkin(){
  checkout = false;
 }
 
 @Override
 protected void finalize() throws Throwable {
  if (checkout) {
   System.out.println("Error: check out");
  }
 }
}

public class FinalizeCheckObjectUse {

 public static void main(String[] args) {
  new Book(true);
  System.gc();
 }

}
실행 결과:
Error: check out
예에서의Book 대상은 방출 전에 반드시 checkin 상태에 있어야 한다. 그렇지 않으면 방출할 수 없다.finalize의 실현은 비합법적인 대상을 제때에 발견하거나 더욱 직접적으로finalize에서 어떤 인용 변수를 직접 사용하여 다시 reachable 상태로 들어가서 다시 처리하는 데 도움을 줄 수 있다.
또 다른 주의해야 할 것은, 하위 클래스가 부모 클래스의finalize 방법을 덮어쓰면, 수동으로 슈퍼를 호출하는 것을 잊어버리는 것입니다.finalize나 하위 클래스의finalize 과정에 이상이 생겨서 슈퍼에 실행되지 않았습니다.finalize 시, 부류의 종결 방법은 영원히 바뀌지 않을 것입니다.
다음과 같습니다.

class Parent{
  @Override
  protected void finalize() throws Throwable {
    System.out.println(getClass().getName() + " finalize start");
  }
}

class Son extends Parent{
  @Override
  protected void finalize() throws Throwable {
    System.out.println(getClass().getName() + " finalize start");
  }
}
public class SuperFinalizeLost {

  public static void main(String[] args) {
    new Son();
    System.gc();
  }

}

실행 결과:
Son finalize start
혹은

class Parent{
  @Override
  protected void finalize() throws Throwable {
    System.out.println(getClass().getName() + " finalize start");
  }
}

class Son extends Parent{
  @Override
  protected void finalize() throws Throwable {
    System.out.println(getClass().getName() + " finalize start");
    int i = 5 / 0;
    super.finalize();
  }
}
public class SuperFinalizeLost {

  public static void main(String[] args) {
    new Son();
    System.gc();
  }

}
실행 결과:
Son finalize start
두 번째 상황에 대해try를 사용할 수 있습니다.finally...구조적으로 해결하지만 첫 번째 상황에 대해서는 종결 방법의 수호자라는 방식을 사용하는 것이 좋다.예는 다음과 같다.

class Parent2{
  private final Object finalizeGuardian = new Object() {
    protected void finalize() throws Throwable {
      System.out.println(" ");
    };
  };
}

class Son2 extends Parent2{
  @Override
  protected void finalize() throws Throwable {
    System.out.println(getClass().getName() + " finalize start");
    int i = 5 / 0;
    super.finalize();
  }
}

public class FinalizeGuardian {

  public static void main(String[] args) {
    new Son2();
    System.gc();
  }

}

실행 결과:
여기서 상위 종결 방법의 논리를 실행합니다
Son2 finalize start
이렇게 하면 부류의 종결 방법에서 필요한 조작이 실행될 것을 보장할 수 있다.
이상은 본문의 전체 내용입니다. 여러분의 학습에 도움이 되기를 바랍니다.

좋은 웹페이지 즐겨찾기