彻底搞懂 Java 内存泄露

Java内存回收方式

Java判断对象是否可以回收使用的而是可达性分析算法。

在主流的商用程序语言中(Java和C#),都是使用可达性分析算法判断对象是否存活的。这个算法的基本思路就是通过一系列名为”GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的,下图对象object5, object6, object7虽然有互相判断,但它们到GC Roots是不可达的,所以它们将会判定为是可回收对象。

在Java语言里,可作为GC Roots对象的包括如下几种:

  • a.虚拟机栈(栈桢中的本地变量表)中的引用的对象
  • b.方法区中的类静态属性引用的对象
  • c.方法区中的常量引用的对象
  • d.本地方法栈中JNI的引用的对象

摘自《深入理解Java虚拟机》

使用leakcanary检测泄漏

关于LeakCanary使用参考以下文章:

  1. LeakCanary: 让内存泄露无所遁形
  2. LeakCanary 中文使用说明

LeakCanary的内存泄露提示一般会包含三个部分:

  • 第一部分(LeakSingle类的sInstance变量)
  • 引用第二部分(LeakSingle类的mContext变量),
  • 导致第三部分(MainActivity类的实例instance)泄露.

leakcanary使用注意

即使是空的Activity,如果检测泄露时候遇到了如下这样的泄露,注意,把refWatcher.watct()放在onDestroy里面即可解决,或者忽略这样的提示。
由于文章已写很多,下面的就不再修改,忽略这种错误即可。

* com.less.demo.TestActivity has leaked:
* GC ROOT static android.app.ActivityThread.sCurrentActivityThread
* references android.app.ActivityThread.mActivities
* references android.util.ArrayMap.mArray
* references array java.lang.Object[].[1]
* references android.app.ActivityThread$ActivityClientRecord.activity
* leaks com.less.demo.TestActivity instance
protected void onDestroy() {
    super.onDestroy();
    RefWatcher refWatcher = App.getRefWatcher(this);
    refWatcher.watch(this);
}

leakcanary和代码示例说明内存泄露

案例一(静态成员引起的内存泄露)

public class App extends Application {
    private RefWatcher refWatcher;
    @Override
    public void onCreate() {
        super.onCreate();
        refWatcher = LeakCanary.install(this);
    }
    public static RefWatcher getRefWatcher(Context context) {
        App application = (App) context.getApplicationContext();
        return application.refWatcher;
    }
}

测试内部类持有外部类引用,内部类是静态的(GC-ROOT,将一直连着这个外部类实例),静态的会和Application一个生命周期,这会导致一直持有外部类引用(内部类隐含了一个成员变量$0), 即使外部类制空= null,也无法释放。

OutterClass

public class OutterClass {
    private String name;
    class Inner{
        public void list(){
            System.out.println("outter name is " + name);
        }
    }
}

TestActivity

public class TestActivity extends Activity {
    // 静态的内部类实例
    private static OutterClass.Inner innerClass;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        OutterClass outterClass = new OutterClass();
        innerClass = outterClass.new Inner();
        RefWatcher refWatcher = App.getRefWatcher(this);
        refWatcher.watch(outterClass);// 监控的对象
        outterClass = null;
    }

案例二(单例模式引起的内存泄露)

DownloadManager

public class DownloadManager {
    private static DownloadManager instance;
    private Task task ;
    public static DownloadManager getInstance(){
        if (instance == null) {
            instance = new DownloadManager();
        }
        return instance;
    }
    public Task newTask(){
        this.task = new Task();
        return task;
    }
}

Task

public class Task {
    private Call call;
    public Call newCall(){
        this.call = new Call();
        return call;
    }
}

Call

public class Call {
    public void execute(){
        System.out.println("=========> execute call");
    }
}

TestActivity

public class TestActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        RefWatcher refWatcher = App.getRefWatcher(this);
        Task task = DownloadManager.getInstance().newTask();
        Call call = task.newCall();
        call.execute();
        refWatcher.watch(call);// 监控的对象
        call = null; // 无法回收,DownloadManager是静态单例,引用task,task引用了call,即使call置为空,也无法回收,切断GC_ROOT 联系即可避免内存泄露,即置task为空。
    }
}

部分日志打印如下:当前的GC_ROOT是DownloadManager的instance实例。

 In com.leakcanary.demo:1.0:1.
* com.less.demo.Call has leaked:
* GC ROOT static com.less.demo.DownloadManager.instance
* references com.less.demo.DownloadManager.task
* references com.less.demo.Task.call
* leaks com.less.demo.Call instance

关于上面两种方式导致的内存泄露问题,这里再举两个案例说明以加强理解。

案例三(静态变量导致的内存泄露)

public class TestActivity extends Activity {
    private static Context sContext;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        sContext = this;
        RefWatcher refWatcher = App.getRefWatcher(this);
        refWatcher.watch(this);// 监控的对象
    }

打印日志如下:

 In com.leakcanary.demo:1.0:1.
com.less.demo.TestActivity has leaked:
GC ROOT static com.less.demo.TestActivity.sContext
leaks com.less.demo.TestActivity instance

从这段日志可以分析出:声明static后,sContext的生命周期将和Application一样长,Activity即使退出到桌面,Application依然存在->sContext依然存在,GC此时想回收Activity却发现Activity仍然被sContext(GC-ROOT连接着),导致死活回收不了,内存泄露。

上面的代码改造一下,如下。

public class TestActivity extends Activity {
    private static View sView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        sView = new View(this);
        RefWatcher refWatcher = App.getRefWatcher(this);
        refWatcher.watch(this);
    }
}

日志如下

In com.leakcanary.demo:1.0:1.
com.less.demo.TestActivity has leaked:
GC ROOT static com.less.demo.TestActivity.sView
references android.view.View.mContext
leaks com.less.demo.TestActivity instance

案例四(单例模式导致的内存泄露)

DownloadManager

public class DownloadManager {
    private static DownloadManager instance;
    private List<DownloadListener> mListeners = new ArrayList<>();
    public interface DownloadListener {
        void done();
    }
    public static DownloadManager getInstance(){
        if (instance == null) {
            instance = new DownloadManager();
        }
        return instance;
    }
    public void register(DownloadListener downloadListener){
        if (!mListeners.contains(downloadListener)) {
            mListeners.add(downloadListener);
        }
    }
    public void unregister(DownloadListener downloadListener){
        if (mListeners.contains(downloadListener)) {
            mListeners.remove(downloadListener);
        }
    }
}

TestActivity

public class TestActivity extends Activity implements DownloadManager.DownloadListener{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        DownloadManager.getInstance().register(this);
        RefWatcher refWatcher = App.getRefWatcher(this);
        refWatcher.watch(this);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 忘记 unregister
        // DownloadManager.getInstance().unregister(this);
    }
    @Override
    public void done() {
        System.out.println("done!");
    }
}

In com.leakcanary.demo:1.0:1.
* com.less.demo.TestActivity has leaked:
* GC ROOT static com.less.demo.DownloadManager.instance
* references com.less.demo.DownloadManager.mListeners
* references java.util.ArrayList.array
* references array java.lang.Object[].[0]
* leaks com.less.demo.TestActivity instance

错误写法一定导致内存泄露吗?

答案是:否定的。

如下案例,有限时间内是可以挽救内存泄露发生的,所以控制好生命周期,其他情况:如单例对象(静态变量)的生命周期比其持有的sContext的生命周期更长时 ->内存泄露,更短时->躲过内存泄露。

public class TestActivity extends Activity {
    private static Context sContext;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        sContext = this;
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                sContext = null;
            }
        },1000);// 分别测试1s和12s,前者不会内存泄露,后者一定泄露。所以如果赶在GC之前切断GC_ROOT是可以避免内存泄露的。
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        RefWatcher refWatcher = App.getRefWatcher(this);
        refWatcher.watch(this);
    }
}

Handler 引起的内存泄露详细分析

handler导致的内存泄露可能我们大多数都犯过.

注意以下代码中的注释,非静态内部类虽然持有外部类引用,但是持有并不代表一定泄露,而是看gc时谁的命长。经过测试 情况(1)始终没有内存泄露。

为什么会这样, 很早阅读Handler源码时候记得这几个货都是互相引用来引用去的,Message有个target字段, message.target = handler;
handler.post(message);又把这个message推入了MessageQueue中,而MessageQueue是在一个Looper线程中不断轮询处理消息,而有时候message还是个老不死,能够重复利用。如果当Activity退出时候,还有消息未处理或正在处理,由于message引用handler,handler又引用Activity,此时将引发内存泄露。

public class TestActivity extends Activity {
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            System.out.println("===== handle message ====");
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        // (1) 不会导致内存泄露
        handler.sendEmptyMessageDelayed(0x123,0);
        // (2) 会导致内存泄露,非静态内部类(包括匿名内部类,比如这个 Handler 匿名内部类)会引用外部类对象 this(比如 Activity)
        // 当它使用了 postDelayed 的时候,如果 Activity 已经 finish 了,而这个 handler 仍然引用着这个 Activity 就会致使内存泄漏
        // 因为这个 handler 会在一段时间内继续被 main Looper 持有,导致引用仍然存在.
        handler.sendEmptyMessageDelayed(0x123, 12000);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        RefWatcher refWatcher = App.getRefWatcher(this);
        refWatcher.watch(this);
    }
}

com.less.demo.TestActivity has leaked:
* GC ROOT android.view.inputmethod.InputMethodManager$ControlledInputConnectionWrapper.mH
* references com.android.internal.view.IInputConnectionWrapper$MyHandler.mQueue
* references android.os.MessageQueue.mMessages
* references android.os.Message.target , matching exclusion field android.os.Message#target
* references com.less.demo.TestActivity$1.this$0 (anonymous subclass of android.os.Handler)
* leaks com.less.demo.TestActivity instance

知道了原理后,即使写出易于内存泄露的代码也能够避免内存泄露啦。

上述代码只需在onDestroy()函数中调用mHandler.removeCallbacksAndMessages(null);

在Activity退出的时候的移除消息队列中所有消息和所有的Runnable。

内部类引起的内存泄露

内部类种类大致如下:

  1. 非静态内部类(成员内部类)
  2. 静态内部类(嵌套内部类)
  3. 局部内部类(定义在方法内或者作用域内的类,好似局部变量,所以不能有访问控制符和static等修饰)
  4. 匿名内部类(没有名字,仅使用一次new个对象即扔掉类的定义)

为什么非静态内部类持有外部类引用,静态内部类不持有外部引用。

这个问题非常简单,就像 static的方法只能调用static的东西,非static可以调用非static和static的一样。static–> 针对class, 非static->针对 对象,我是这么简单理解的。看图:

匿名内部类

将局部内部类的使用再深入一步,假如只创建某个局部内部类的一个对象,就不必命名了。

匿名内部类的类型可以是如下几种方式。

  1. 接口匿名内部类
  2. 抽象类匿名内部类
  3. 类匿名内部类

匿名内部类总结:

  1. 其实主要就是类定义一次就失效了,主要使用的是这个类(不知道名字)的实例。根据内部类的特性,能够实现回调和闭包
  2. JavaScript和Python的回调传递的是fuction,Java传递的是object。
    Java中常常用到回调,而回调的本质就是传递一个对象,JavaScript或其他语言则是传递一个函数(如Python,或者C,使用函数指针的方式),由于传递一个对象可以携带其他的一些信息,所以Java认为传递一个对象比传递一个函数要灵活的多(当然java也可以用Method反射对象传递函数)。参考《Java核心技术》

非静态内部类导致内存泄露(前提dog的命长)

下面的案例将导致内存泄露

其中private static Dog dog ; 导致Dog的命比TestActivity长,这就糟糕了,但是注意,如果改为private Dog dog ; 即使Dog是非静态内部类,也不会导致内存泄露,要死也是Dog先死,毕竟Dog是TestActivity的家庭成员,开挂也得看主人。

public class TestActivity extends Activity {
    private static Dog dog ;
    class Dog {
        public void say(){
            System.out.println("I am lovely dog!");
        }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        dog = new Dog();
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        RefWatcher refWatcher = App.getRefWatcher(this);
        refWatcher.watch(this);
    }
}

In com.leakcanary.demo:1.0:1.
* com.less.demo.TestActivity has leaked:
* GC ROOT static com.less.demo.TestActivity.dog
* references com.less.demo.TestActivity$Dog.this$0
* leaks com.less.demo.TestActivity instance

哪些内部类或者回调函数是否持有外部类对象?

一个反射案例说明一切

/**
 * 作者: limitless
 * 描述: 一个案例测试所有类型内部类对外部类对象的持有情况
 * 网站: https://github.com/wangli0
 */
public class Main {
    /* 持有外部类引用 */
    private IAppListener mAppListener = new IAppListener() {
        private String name;
        @Override
        public void done() {
            System.out.println("匿名内部类对象作为成员变量");
        }
    };
    /* 未持有 */
    private static IAppListener sAppListener = new IAppListener() {
        private String name;
        @Override
        public void done() {
            System.out.println("匿名内部类对象作为static成员变量");
        }
    };
    public static void main(String args[]) {
        Main main = new Main();
        main.test1();
        main.test2();
        main.test3();// test3 《=》test4
        main.test4();
        main.test5();
        main.test6();
    }
    class Dog {
        private String name;
    }
    /* 持有外部类引用 */
    public void test1(){
        Dog dog = new Dog();
        getAllFieldName(dog.getClass());
    }
    static class Cat {
        private String name;
    }
    /* 未持有 */
    private void test2() {
        Cat cat = new Cat();
        getAllFieldName(cat.getClass());
    }
    /* 持有外部类引用 */
    private void test3() {
        class Monkey{
            String name;
        }
        Monkey monkey = new Monkey();
        getAllFieldName(monkey.getClass());
    }
    /* 持有外部类引用 */
    private void test4() {
        // 常用作事件回调的地方(有可能引起内存泄露)
        IAppListener iAppListener = new IAppListener() {
            private String name;
            @Override
            public void done() {
                System.out.println("匿名内部类");
            }
        };
        getAllFieldName(iAppListener.getClass());
    }
    /* 持有外部类引用 */
    private void test5() {
        getAllFieldName(mAppListener.getClass());
    }
    /* 未持有 */
    private void test6() {
        getAllFieldName(sAppListener.getClass());
    }
    private void getAllFieldName(Class<?> clazz) {
        System.out.println("className: ======> " + clazz.getSimpleName());
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field.getName());
        }
    }
}

上述结果足够说明,即使是方法内的回调对象也是持有外部类引用的,那么虽然作用域是局部的,也有存在内存泄露的可能。我多次强调 持有某对象 不代表一定泄露,看的是谁命长。回调在Android开发过程中几乎处处可见,如果持有就会内存泄露的话,那几乎就没法玩了。

一般情况下,我们常常设置某个方法内的xx.execute(new Listener(){xx});是不会导致内存泄露的,这个方法执行完,局部作用域就失效了。但是如果在execute(listener);过程中,某个单例模式的对象 突然引用了这个listener对象,那么就会导致内存泄露。

下面用实例证明我的想法

TestActivity

public class TestActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        Task task = new Task();
        task.execute(new ICallback() {
            @Override
            public void done() {
                System.out.println("下载完成!");
            }
        });
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        RefWatcher refWatcher = App.getRefWatcher(this);
        refWatcher.watch(this);
    }
}

Task

public class Task {
    public void execute(ICallback iCallback) {
        DownloadManager.getInstance().execute(iCallback);
    }
}

DownloadManager

public class DownloadManager {
    public static DownloadManager instance;
    private ICallback mICallback;
    public static DownloadManager getInstance(){
        if (instance == null) {
            instance = new DownloadManager();
        }
        return instance;
    }
    public void execute(ICallback iCallback) {
        this.mICallback = iCallback;// 反例,千万不要这么做,将导致内存泄露,如果注释掉这行,内存泄露不会发生
        iCallback.done();
    }

这足以证明我的想法是正确的,Callback的不巧当使用同样会导致内存泄露 的发送。

总结

  1. 如果某些单例需要使用到Context对象,推荐使用Application的context,不要使用Activity的context,否则容易导致内存泄露。单例对象的生命周期和Application一致,这样Application和单例对象就一起销毁。
  2. 优先使用静态内部类而不是非静态的,因为非静态内部类持有外部类引用可能导致垃圾回收失败。如果你的静态内部类需要宿主Activity的引用来执行某些东西,你要将这个引用封装在一个WeakReference中,避免意外导致Activity泄露,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收 只被弱引用关联 的对象,只被 说明这个对象本身已经没有用处了。
public class TestActivity extends Activity {
    private MyHandler myHandler = new MyHandler(this);
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);

    }
    static class MyHandler extends Handler {
        private WeakReference<Activity> mWeakReference;
        public MyHandler(Activity activity){
            mWeakReference = new WeakReference<Activity>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Toast.makeText(mWeakReference.get(), "xxxx", Toast.LENGTH_LONG).show();
            Log.d("xx", mWeakReference.get().getPackageName());
        }
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        RefWatcher refWatcher = App.getRefWatcher(this);
        refWatcher.watch(this);
    }
}

欢迎加入学习交流群569772982,大家一起学习交流。
时间: 2024-08-28 09:19:40

彻底搞懂 Java 内存泄露的相关文章

Java内存泄露的理解与解决

Java内存管理机制 在C++语言中,如果需要动态分配一块内存,程序员需要负责这块内存的整个生命周期.从申请分配.到使用.再到最后的释放.这样的过程非常灵活,但是却十分繁琐,程序员很容易由于疏忽而忘记释放内存,从而导致内存的泄露.Java语言对内存管理做了自己的优化,这就是垃圾回收机制.Java的几乎所有内存对象都是在堆内存上分配(基本数据类型除外),然后由GC(garbage collection)负责自动回收不再使用的内存. 上面是Java内存管理机制的基本情况.但是如果仅仅理解到这里,我们

java内存泄露详解

很多人有疑问,java有很好的垃圾回收机制,怎么会有内存泄露?其实是有的,那么何为内存泄露?在Java中所谓内存泄露就是指在程序运行的过程中产生了一些对象,当不需要这些对象时,他们却没有被垃圾回收掉,而且程序运行中很难发现这个对象,它始终占据着内存却没有发挥作用. 我举这样一个例子,在现实开发中我们需要自定义一个先进后出的栈集合,代码如下: 这个代码看起来和运行起来都没问题,但是,这里有个很隐晦的问题,就是在pop()方法里面,我们首先找到集合最后一个元素的下标,然后按照下标从集合中取出,但是这

关于java内存泄露的总结--引用的类型:强引用,弱引用,软引用

今天面试了一家公司的java开发方面的实习生,被问到一个问题:如何处理java中的内存泄露问题,保证java的虚拟机内存不会被爆掉,当时其实觉得面试官的问题有点泛,所以也没有很好领会他的意思,答案也不是很准确,后来回去查了下资料,大概明白面试官要问的东西是什么(尴尬,才反应过来),然后,也特地简单总结下java内存溢出的相关内容,以备之后复习. 一.什么情况下会java内存泄露? java不是有GC吗?为毛还会内存泄露?之前我也一直以为,java有垃圾回收器在,估计内存泄露的情况一般不会发生吧?

Java 内存泄露(二)

一.Java内存回收机制 不论哪种语言的内存分配方式,都需要返回所分配内存的真实地址,也就是返回一个指针到内存块的首地址.Java中对象是采用new或者反射的方法创建的(还有克隆),这些对象的创建都是在堆(Heap)中分配的,所有对象的回收都是由Java虚拟机通过垃圾回收机制完成的.GC为了能够正确释放对象,会监控每个对象的运行状况,对他们的申请.引用.被引用.赋值等状况进行监控,Java会使用有向图的方法进行管理内存,实时监控对象是否可以达到,如果不可到达,则就将其回收,这样也可以消除引用循环

黑马程序员——hashCode方法的作用,java内存泄露

hashCode方法的作用:当有一个对象要存入hash集合的时候,JVM首先会调用hashCode方法获取该对象的哈希值,然后根据哈希值找到相应的存储区域,最后取出该区域的所有元素与该对象进行equals比较,如果相等,不存入该元素,否则,存入.这样不用遍历集合中的所有元素就能的到我们想要的结果,提高了查找的效率.但是如果不覆写hashCode方法的话,相同的对象可能会存储在HashSet集合中,虽然他们equals比较相同,但他们的内存区域不同的话,就不会进行equals比较了.为了让两个相同

Java内存泄露及性能调优

内存泄漏及解决方法 1.系统崩溃前的一些现象:每次垃圾回收的时间越来越长,由之前的10ms延长到50ms左右,FullGC的时间也有之前的0.5s延长到4.5sFullGC的次数越来越多,最频繁时隔不到1分钟就进行一次FullGC年老代的内存越来越大并且每次FullGC后年老代没有内存被释放 之后系统会无法响应新的请求,逐渐到达OutOfMemoryError的临界值. 2.生成堆的dump文件 通过JMX的MBean生成当前的Heap信息,大小为一个3G(整个堆的大小)的hprof文件,如果没

java 内存泄露 学习

1.什么事内存泄露 内存泄露就是对象不用了,但是无法回收该对象占用的内存. 2.java不是有垃圾回收机制,咋还会发生内存泄露呢? java的垃圾回收机制就是把无用的对象(GC roots可达与否)用的内存回收,但是垃圾回收不是万能的,在一些场合会出现内存泄露, 长对象持有短对象的引用, 1.静态集合类 1 Static Vector v = new Vector(10); 2 for (int i = 1; i<100; i++) 3 { 4 Object o = new Object();

Java 内存泄露的理解与解决过程

本文详细地介绍了Java内存管理的原理,以及内存泄露产生的原因,同时提供了一些列解决Java内存泄露的方案,希望对各位Java开发者有所帮助. Java内存管理机制 在C++ 语言中,如果需要动态分配一块内存,程序员需要负责这块内存的整个生命周期.从申请分配.到使用.再到最后的释放.这样的过程非常灵活,但是却十分繁琐,程序员很容易由于疏忽而忘记释放内存,从而导致内存的泄露. Java 语言对内存管理做了自己的优化,这就是垃圾回收机制. Java 的几乎所有内存对象都是在堆内存上分配(基本数据类型

JAVA 内存泄露详解

一.Java内存回收机制  不论哪种语言的内存分配方式,都需要返回所分配内存的真实地址,也就是返回一个指针到内存块的首地址.Java中对象是采用new或者反射的方法创建的,这些对象的创建都是在堆(Heap)中分配的,所有对象的回收都是由Java虚拟机通过垃圾回收机制完成的.GC为了能够正确释放对象,会监控每个对象的运行状况,对他们的申请.引用.被引用.赋值等状况进行监控,Java会使用有向图的方法进行管理内存,实时监控对象是否可以达到,如果不可到达,则就将其回收,这样也可以消除引用循环的问题.在