Java/Android 참조 유형 및 사용 전반에 대한 분석

13927 단어 java참조 유형
Java/Android에는 다음과 같은 네 가지 참조 유형이 있습니다.
Strong reference - 강제 참조
Soft Reference - 소프트 참조
Weak Reference - 약인용
Phantom Reference - 거짓 참조
서로 다른 인용 유형은 서로 다른 특성을 가지고 서로 다른 사용 장면에 대응한다.
1. Strong reference - 강제 참조
실제 인코딩에서 가장 흔히 볼 수 있는 인용 형식일반적인 형식은 다음과 같다. A a = new A ().기다리다강제 인용 자체는 창고 메모리에 저장되며, 메모리에 저장된 대상의 주소를 가리킨다.일반적인 상황에서 메모리의 대상에 대해 더 이상 강한 인용이 가리키지 않을 때, 쓰레기 회수기는 이 메모리에 대한 쓰레기 회수를 고려하기 시작한다.인코딩을 할 때: a=null, 이 때, 방금 더미에 주소를 분배하고 새로 만든 a대상은 다른 인용이 없습니다. 시스템이 쓰레기 회수를 할 때, 더미 메모리는 쓰레기로 회수됩니다.
SoftReference, WeakReference, PhantomReference는 모두 클래스java입니다.lang.ref.Reference의 하위 클래스입니다.Reference는 추상적인 기본 클래스로서 하위 클래스 대상의 기본 동작을 정의합니다.참조 하위 클래스는 다음과 같은 특징을 가지고 있습니다.
1. Reference 하위 클래스는 참조하지 않고 직접 생성할 수 없으며 최소한 강한 인용 대상을 구조 매개 변수로 하여 각각의 하위 클래스 대상을 생성해야 한다.
2. 1에서 강한 인용 대상을 구조 매개 변수로 대상을 만들기 때문에 원래 강한 인용이 가리키는 메모리에 있는 대상은 강한 인용 자체만 직접적으로 관련되지 않고 Reference의 하위 클래스 대상의 인용과도 일정한 관계가 있다.또한 이러한 연결은 대상의 쓰레기 회수에 영향을 미칠 수 있다.
서로 다른 하위 클래스 대상이 지시 대상(지키는 메모리 중의 대상을 강제로 인용하는 것)의 쓰레기 회수에 대한 서로 다른 영향 특징에 따라 각각 세 개의 하위 클래스, 즉 Soft Reference, Weak Reference와 Phantom Reference가 형성되었다.
2. Soft Reference - 소프트 참조
소프트 참조의 일반적인 사용법은 다음과 같습니다.
A a = new A();
SoftReferencesrA = new SoftReference(a);객체의 강제 참조를 매개 변수로 사용하여 SoftReference 객체를 만들고 스택 메모리의 wrA가 이 객체를 가리키도록 합니다.이때 다음과 같은 인코딩을 진행한다. a=null, 원래 a가 가리키는 A 대상의 쓰레기 회수에 어떤 영향을 줍니까?먼저 다음 프로그램의 출력 결과를 직접 보십시오. import java.lang.ref.SoftReference; public class ReferenceTest { public static void main(String[] args) { A a = new A(); SoftReference<A> srA = new SoftReference<A>(a); a = null; if (srA.get() == null) { System.out.println("a 대상이 쓰레기 회수 절차에 들어간다"); } else { System.out.println ("a 객체가 회수되지 않았습니다" + srA.get (); } // 쓰레기 수거 System.gc(); if (srA.get() == null) { System.out.println("a 대상이 쓰레기 회수 절차에 들어간다"); } else { System.out.println ("a 객체가 회수되지 않았습니다" + srA.get (); } } } class A { }## 출력 결과: 1a 객체가 회수되지 않음A@4807ccf6 2a 객체가 회수되지 않음A@4807ccf6 a =null 이후, 메모리에 쌓인 A 대상은 더 이상 강한 인용이 가리키지 않지만, 이 패션은 srA 인용의 대상이 A 대상을 가리키는 것이 존재한다.srA를 처음 호출할 때get () 방법은 이 지시 대상을 되돌려줍니다. 쓰레기 수거기가 쓰레기 수거를 하지 않았을 가능성이 높기 때문에 get () 는 결과가 있습니다. 이것은 이해하기 쉽습니다.프로그램이 시스템을 실행할 때.gc();강제 쓰레기 회수 후 srA를 통해get (), A객체가 쓰레기로 수거되지 않았음을 나타내는 표시된 A객체를 발견합니다.그렇다면 소프트 인용이 지시한 대상은 언제부터 쓰레기로 회수되기 시작할까?다음과 같은 두 가지 조건을 충족해야 합니다.1. 지시된 대상이 강한 인용 대상이 가리키지 않는 경우.2. 가상 머신의 메모리가 부족할 때.따라서 Soft Reference는 지시 대상이 메모리를 차지하는 시간을 연장하고 가상 컴퓨터의 메모리가 부족할 때까지 쓰레기 수거기가 이 메모리 공간을 회수한다.3. Weak Reference - 약인용마찬가지로 소프트 참조의 일반적인 사용법은 다음과 같습니다.A a = new A();WeakReferencewrA = new WeakReference(a);
이 대상을 가리키는 강력한 인용이 없을 때, 쓰레기 회수는 어떤 특성을 가지고 있습니까?

import java.lang.ref.WeakReference;

public class ReferenceTest {

  public static void main(String[] args) {

    A a = new A();

    WeakReference<A> wrA = new WeakReference<A>(a);

    a = null;

    if (wrA.get() == null) {
      System.out.println("a ");
    } else {
      System.out.println("a " + wrA.get());
    }

    //  
    System.gc();

    if (wrA.get() == null) {
      System.out.println("a ");
    } else {
      System.out.println("a " + wrA.get());
    }

  }

}

class A {

}
## 출력 결과:

a A@52e5376a
a 
출력의 첫 번째 결과는 위에서 설명한다.쓰레기 회수 후 wrA.get () 은null로 돌아가서 지시 대상이 쓰레기 회수 과정에 들어갔음을 나타냅니다.따라서 약인용의 특징은 다음과 같다.
WeakReference는 기존 강제 인용 대상의 쓰레기 회수 시기를 바꾸지 않습니다. 강제 인용 대상이 없으면 이 대상은 정상적인 쓰레기 회수 절차에 들어갑니다.
그렇다면 이 특징에 따라 Weak Reference가 존재하는 것이 무슨 의미가 있는지 의문이 들 가능성이 높다.
그 주요한 사용 장면은 다음과 같다. 현재 강력한 인용이 강한 인용 대상을 가리키고 있다. 이때 업무 수요로 인해 이 대상에 대한 인용을 늘려야 하는 동시에 이 인용의 쓰레기 회수 시기를 바꾸기를 원하지 않는다. 이때 Weak Reference는 수요에 부합되고 생명주기와 관련된 장면에서 흔히 볼 수 있다.
다음은 Android에서 Weak Reference가 사용하는 장면인 정적 내부 클래스와 Weak Reference를 결합하여 Activity에 존재할 수 있는 Handler 메모리 유출 문제를 해결한다.
Activity에서 데이터를 가져오려면 handler-sendMessage 방식을 사용해야 합니다.다음은 이 프로세스의 일반적인 코드입니다.

public class MainActivity extends Activity {

  //...
  private int page;
  private Handler handler = new Handler() {

    @Override
    public void handleMessage(Message msg) {
      if (msg.what == 1) {

        //...

        page++;
      } else {

        //...

      }

    };
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    //...

    new Thread(new Runnable() {
      @Override
      public void run() {
        //.. 
        Message msg = Message.obtain();
        msg.what = 1;
        //msg.obj = xx;
        handler.sendMessage(msg);
      }
    }).start();

    //...

  }

}
Eclispe에서 Run Link에서 경고 메시지가 표시됩니다. This Handler class should be static or leaks might occur...이 정보를 보시려면 클릭하십시오. 자세한 내용은 문제에 대해 설명하고 건의적인 해결 방안을 제시합니다.

Issue: Ensures that Handler classes do not hold on to a reference to an outer class
Id: HandlerLeak

Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class;In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.
대체적으로 Handler를 내부 정적 클래스로 정의하고 이 정적 내부 클래스에서 WeakReference의 인용을 정의하는 것을 권장합니다. 외부의 활동 대상을 가리키기 때문입니다.
문제 분석:
Activity는 자신의 생명 주기를 가지고 있습니다. Activity에서 새로 시작한 스레드가 실행되는 과정에서 사용자가 Back 키를 눌렀거나 시스템 메모리가 부족한 등 이 Activity를 회수하기를 원할 수 있습니다. Activity에서 새로 시작한 스레드가 Activity 자체의 어떤 주기를 따르지 않기 때문입니다. 즉, Activity가 onDestroy를 실행했을 때 스레드와 Handler의 Handle Message의 존재로 인해시스템이 이 Activity 메모리 회수를 원하지만 실현할 수 없습니다. 비정적 내부 클래스에서 외부 클래스에 대한 은밀한 인용을 가지고 있기 때문에 존재할 수 있는 메모리 유출 문제가 있습니다.
따라서 액티비티에서 Handler를 사용할 때 한편으로는 이를 정적 내부 클래스 형식으로 정의해야 한다. 이렇게 하면 외부 클래스(Activity)와 결합을 풀고 외부 클래스의 인용을 가지지 않을 수 있다. 또한 Handler의 Handler Message는 일반적으로 액티비티의 속성에 접근하거나 수정해야 하기 때문에 이 액티비티를 가리키는 Weak Reference를 Handler 내부에서 정의해야 한다.Activity의 메모리 회수에 영향을 주지 않으면서 정상적인 상황에서 Activity의 속성에 접근할 수 있습니다.
Google 공식 권장 사항:

public class MainActivity extends Activity {

  //...
  private int page;
  private MyHandler mMyHandler = new MyHandler(this);

  private static class MyHandler extends Handler {

    private WeakReference<MainActivity> wrActivity;

    public MyHandler(MainActivity activity) {
      this.wrActivity = new WeakReference<MainActivity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
      if (wrActivity.get() == null) {
        return;
      }
      MainActivity mActivity = wrActivity.get();
      if (msg.what == 1) {

        //...
        mActivity.page++;

      } else {

        //...

      }
    }

  }

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    //...

    new Thread(new Runnable() {
      @Override
      public void run() {
        //.. 
        Message msg = Message.obtain();
        msg.what = 1;
        //msg.obj = xx;
        mMyHandler.sendMessage(msg);
      }
    }).start();

    //...

  }

}
Soft Reference와 Weak Reference에 대해 ReferenceQueue라는 구조기 파라미터가 있습니다. Soft Reference나 Weak Reference가 지시하는 대상이 쓰레기로 회수되면 참조는 ReferenceQueue에 배치됩니다.위의 글에서 Soft Reference나 Weak Reference의 get () 방법이null로 되돌아올 때, 지시된 대상이 쓰레기 회수 절차에 들어갔음을 나타낼 뿐, 대상이 반드시 쓰레기 회수된 것은 아닙니다.쓰레기가 수거된 것을 확인한 후에만 ReferenceQueue가 있으면 참조는 ReferenceQueue에 저장됩니다.
다음 예제를 참조하십시오.

public class ReferenceTest {

  public static void main(String[] args) {

    A a = new A();

    WeakReference<A> wrA = new WeakReference<A>(a);

    a = null;

    if (wrA.get() == null) {
      System.out.println("a ");
    } else {
      System.out.println("a " + wrA.get());
    }

    //  
    System.gc();

    if (wrA.get() == null) {
      System.out.println("a ");
    } else {
      System.out.println("a " + wrA.get());
    }

  }
}

class A {

  @Override
  protected void finalize() throws Throwable {
    super.finalize();
    System.out.println("in A finalize");
  }

}
## 출력 결과:

1 a A@46993aaa
2 a 
3 in A finalize
이로써 위의 쓰레기 회수 절차에 들어갔다는 주장도 검증됐다.다음은 ReferenceQueue를 결합하여 코드를 살펴보겠습니다.

public class ReferenceTest {

  public static void main(String[] args) {

    A a = new A();

    ReferenceQueue<A> rq = new ReferenceQueue<A>();
    WeakReference<A> wrA = new WeakReference<A>(a, rq);

    a = null;

    if (wrA.get() == null) {
      System.out.println("a ");
    } else {
      System.out.println("a " + wrA.get());
    }

    System.out.println("rq item:" + rq.poll());

    //  
    System.gc();

    if (wrA.get() == null) {
      System.out.println("a ");
    } else {
      System.out.println("a " + wrA.get());
    }

    /*
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    */

    System.out.println("rq item:" + rq.poll());

  }
}

class A {

  @Override
  protected void finalize() throws Throwable {
    super.finalize();
    System.out.println("in A finalize");
  }

}
## 출력 결과:

a A@302b2c81
rq item:null
a 
rq item:null
in A finalize
이를 통해 "쓰레기 수거 프로세스에만 들어가는 Soft Reference 또는 Weak Reference 참조가 ReferenceQueue에 추가되지 않았습니다."를 검증했습니다.


public class ReferenceTest {

  public static void main(String[] args) {

    A a = new A();

    ReferenceQueue<A> rq = new ReferenceQueue<A>();
    WeakReference<A> wrA = new WeakReference<A>(a, rq);

    a = null;

    if (wrA.get() == null) {
      System.out.println("a ");
    } else {
      System.out.println("a " + wrA.get());
    }

    System.out.println("rq item:" + rq.poll());

    //  
    System.gc();

    if (wrA.get() == null) {
      System.out.println("a ");
    } else {
      System.out.println("a " + wrA.get());
    }

    try {
      Thread.sleep(1);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    System.out.println("rq item:" + rq.poll());

  }
}

class A {

  @Override
  protected void finalize() throws Throwable {
    super.finalize();
    System.out.println("in A finalize");
  }

}
## 출력 결과:

a A@6276e1db
rq item:null
a 
in A finalize
rq item:java.lang.ref.WeakReference@645064f
이로써 상술한 견해를 실증하였다.
4.PhantomReference
PhantomReference는 SoftReference 또는 WeakReference와 비교할 때 다음과 같은 주요 차이점을 나타냅니다.
1. PhantomReference는 구조 함수인 PhantomReference(T referent, ReferenceQueueq)만 있기 때문에 PhantomReference 사용은 반드시 ReferenceQueue를 결합해야 한다.
2. PhantomReference를 가리키는 지시 대상에 대한 강한 인용이 있든 없든 PhantomReference의 get () 방법은 결과를 null로 되돌려줍니다.

public class ReferenceTest {

  public static void main(String[] args) {

    A a = new A();

    ReferenceQueue<A> rq = new ReferenceQueue<A>();
    PhantomReference<A> prA = new PhantomReference<A>(a, rq);

    System.out.println("prA.get():" + prA.get());
    
    a = null;
    
    System.gc();
    
    try {
      Thread.sleep(1);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    System.out.println("rq item:" + rq.poll());

  }
}

class A {

}
## 출력 결과:

prA.get():null
rq item:java.lang.ref.PhantomReference@1da12fc0
코드의 Thread.sleep(1);작용은 전례와 마찬가지로 쓰레기 회수 라인이 실행될 수 있도록 확보하는 것이다.그렇지 않으면 쓰레기 수거 절차에 들어가서 쓰레기 수거를 지시하지 않은 대상의 허위 인용은 Phantom Reference에 추가되지 않습니다.
Weak Reference와 마찬가지로 Phantom Reference는 지시 대상의 쓰레기 수거 시기를 바꾸지 않습니다.또한 요약할 수 있듯이 ReferenceQueue의 역할은 주로 SoftReference/WeakReference/PhantomReference의 지시 대상이 쓰레기로 회수되었는지 감청하는 데 사용된다.
지금까지 여러분께 소개된 Java/Android 인용 유형과 그 사용에 대한 전면적인 분석의 모든 내용입니다. 여러분께 도움이 되고 많은 응원 부탁드립니다~

좋은 웹페이지 즐겨찾기