二 、JVM (Java对象、对象引用类型)

二 、JVM (Java对象、对象引用类型)

绿林寻猫
2021-12-08 / 0 评论 / 159 阅读 / 正在检测是否收录...

目录

 

Java对象的大小

测试

对象引用类型

强引用

软引用

弱引用

虚引用 


Java对象的大小

这里要使用的是lucene提供的专门用于计算堆内存占用大小的工具类:RamUsageEstimator

<dependency>
      <groupId>org.apache.lucene</groupId>
      <artifactId>lucene-core</artifactId>
      <version>4.0.0</version>
</dependency>


使用该第三方工具比较简单直接,主要依靠JVM本身环境、参数及CPU架构计算头信息,再依据数据类型的标准计算实例字段大小,计算速度很快,另外使用较方便。如果非要说这种方式有什么缺点的话,那就是这种方式计算所得的对象头大小是基于JVM声明规范的,并不是通过运行时内存地址计算而得,存在与实际大小不符的这种可能性。
常用API

//计算指定对象及其引用树上的所有对象的综合大小,单位字节
long RamUsageEstimator.sizeOf(Object obj)
 
//计算指定对象本身在堆空间的大小,单位字节
long RamUsageEstimator.shallowSizeOf(Object obj)
 
//计算指定对象及其引用树上的所有对象的综合大小,返回可读的结果,如:2KB
 
String RamUsageEstimator.humanSizeOf(Object obj)


测试

    public static void main(String[] args) {
        Object o = new Object();
        //计算指定对象及其引用树上的所有对象的综合大小,单位字节
        System.out.println(RamUsageEstimator.sizeOf(o));
        //计算指定对象本身在堆空间的大小,单位字节
        System.out.println(RamUsageEstimator.shallowSizeOf(o));
        //计算指定对象及其引用树上的所有对象的综合大小,返回可读的结果,如:2KB
        System.out.println(RamUsageEstimator.humanSizeOf(o));

        System.out.println("一个Interger对象大小为:"+RamUsageEstimator.sizeOf(new Integer(1)));
        System.out.println("一个String对象大小为:"+RamUsageEstimator.sizeOf(new String("a")));
        System.out.println("一个char对象大小为:"+RamUsageEstimator.sizeOf(new char[1]));
        System.out.println("一个ArrayList对象大小为:"+RamUsageEstimator.sizeOf(new ArrayList<>()));
        System.out.println("一个Object对象大小为:"+RamUsageEstimator.sizeOf(new Object()));
        System.out.println("一个Long对象大小为:"+RamUsageEstimator.sizeOf(new Long(10000000000L)));

    }


结果:
16
16
16 bytes
一个Interger对象大小为:16
一个String对象大小为:48
一个char对象大小为:24
一个ArrayList对象大小为:40
一个Object对象大小为:16
一个Long对象大小为:24


对象引用类型

从高到低依次:

  • 强引用(StrongReference)

  • 软引用(SoftReference)

  • 弱引用(WeakReference)

  • 虚引用(PlantomReference)

 

强引用

就是我们一般声明对象是时虚拟机生成的引用,强引用环境下,JVM宁可抛出OOM(out of memory)异常也不会回收强引用所指向的对象,即GC(垃圾回收或垃圾收集)绝对不会回收强引用类型。由于GC绝不会回收强引用,所以它将可能导致内存泄漏。

例:

     //object和str都是强引用
     Object object = new Object();
     String str = "hello";
     String s = new String()


软引用

软引用用于描述一些还有用但并非必须的对象,一般被做为缓存来使用(比如网页缓存、图片缓存)。与强引用的区别是,软引用在垃圾回收时,虚拟机会根据当前系统的剩余内存来决定是否对软引用进行回收。如果剩余内存比较紧张,则虚拟机会回收软引用所引用的空间;如果剩余内存相对富裕,则不会进行回收。换句话说,虚拟机在发生OutOfMemory时,肯定是没有软引用存在的。

例:

        SoftReference<String> sr = new SoftReference(new String("123"));
        System.out.println(sr.get());


注意

在垃圾回收器对这个Java对象回收前,SoftReference类所提供的get方法会返回Java对象的强引用,一旦垃圾线程回收该Java对象之后,get方法将返回null。所以在获取软引用对象的代码中,一定要判断是否为null,以免出现NullPointerException异常导致应用崩溃。

弱引用

弱引用用于实现一些规范化映射(WeakHashMap),其中key或者value当它们不再被引用时可以自动被回收。当你想引用一个对象,但是这个对象有自己的生命周期,你又不想介入这个对象的生命周期时,便可以用弱引用。同时弱引用也可以用来保存那些可有可无的缓存数据,和软引用一样,在内存充足时加速系统,在内存不足时便被系统回收。弱引用在系统GC时,只要一被发现,不管系统堆内存空间是否足够,都会将对象进行回收,一旦被回收便会被加入到相关联的引用队列中。但是,由于垃圾回收器的线程通常优先级很低,所以并不一定能很快发现持有弱引用的对象,所以弱引用的生命周期是从被创建到下一次GC发送之前。
例:

        WeakReference<String> wr = new WeakReference(new String("123"));
        System.out.println(wr.get());
        System.gc();                //通知JVM的gc进行垃圾回收
        System.out.println(wr.get());


注意

第二个输出结果是null,这说明只要JVM进行垃圾回收,被弱引用关联的对象必定会被回收掉。不过要注意的是,这里所说的被弱引用关联的对象是指只有弱引用与之关联,如果存在强引用同时与之关联,则进行垃圾回收时也不会回收该对象(软引用也是如此)。

虚引用 

虚引用又叫幽灵或幻影引用,主要用来跟踪对象被垃圾回收器回收清理的活动,提供比Java清理机制更灵活的处理方式。虚引用是所有引用类型中最弱的一个,如果一个对象拥有一个虚引用,那它便和没有被引用一样,随时可能被GC回收,而且GC在回收时会直接销毁持有该引用的对象,并把虚引用加入引用队列中。除此之外需要注意的一点是,当虚引用试图通过get()方法取得强引用时,无论强引用是否存在,总是会失败,即获得的返回值永远为null。

虚引用与软引用和弱引用的区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
例:将虚引用和引用队列结合使用,可以看到虚引用的对象被垃圾回收后,虚引用将被添加到引用队列

        String str =new String("123");
        ReferenceQueue rq=new ReferenceQueue();
        WeakReference wr=new WeakReference(str);
        PhantomReference pr=new PhantomReference(str,rq);
        Object obj=wr.get();
        System.out.println(obj);
        System.out.println(wr.get());
        str=null;
        System.out.println(pr.get());
        System.gc();
        System.runFinalization();
        System.out.println(rq.poll()==pr);
        //rq.poll()函数作用为检索并移除文件的头
        //rq.poll();
        System.out.println(rq.poll()==pr);

结果:
123
123
null
false
false


 

 

0

评论 (0)

取消