博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
对于一些手机内存概念的思考、深入理解java的finalize,对于内存优化的小总结...
阅读量:6865 次
发布时间:2019-06-26

本文共 3901 字,大约阅读时间需要 13 分钟。

hot3.png

最近在对手机性能做了一些小概念总结,方便对于自己的个人思考

Shallow Size:

对象本身占用的内存空间,不包含其引用的对象,但在JAVA中除基本类型外,一切均为对象,也就是说持有的一直为对象的引用,如String类型对象,它主要包含3个int成员(3*4B)、1个char[]成员(1*4B)以及一个对象头(8B),尽管char[]可能指向一大块字符,但String对象里只有一个引用所占4B的空间,因此String类型对象的Shallow Size就是12B+4B+8B = 24B。

 

Retained Size:

对象本身的Shallow Size + 对象能直接或间接访问到的对象的Shallow Size,也就是说Retained Size就是该对象被gc之后所能回收内存的总和。

 

为了更好地理解Retained Size,不妨以图1加以说明,图中每个节点对应内存中的一个对象,但注意这里有个特殊的节点GC Roots(详见注解),它也是Reference chain的起点。在图3中,从对象obj1入手,可知通过obj1可以直接或间接访问的对象为蓝色节点,而左图中obj3是GC Roots可达的,也就是说断掉obj1到obj3的Reference chain后,obj3还有GC Roots的Reference chain,因而在gc时obj1被回收也不会回收obj3,因此在左图obj1的obj1的Retained Size = obj1 + obj2 + obj4 的shallow size,而右图obj1的Retained Size = obj1 + obj2 + obj4 +obj3 的shallow size.总之,Retained size是一个整体度量,能反映内存结构和对象图的依赖关系,还可以找到根节点。

 

图1 Retained Size示例图

注:GC Roots特指垃圾收集器对象,JVM GC时会回收那些不是GC roots且没有被GC roots引用的对象。JVM对内存中对象进行回收,主要是通过GC Roots Tracing来辨别对象是否在使用,进而进行回收。也就是说通过一系列名为“GC Roots”的对象作为起始点,从节点向下搜索,搜索过的路径称之为Reference Chain,当一个对象到GC Roots没有任何Reference Chain相连时,则表明此对象不可用,GC时将被回收(该流程的示例图见图2)。

 

图2.GC Roots Tracing回收机制

 

  Heap Size: 

堆的大小,当资源增加,当前堆的空间不够时,系统会增加堆的大小,若超过上限(如64M,阈值视平台而定)则会被杀掉

 Allocated:

堆中已分配的大小,即App应用实际占用的内存大小,资源回收后,此项数据会变小。

关于gc过程,我们又要看一个finalize方法,可以更好的理解gc

1 的GC只负责内存相关的清理,所有其它资源的清理必须由程序员手工完成。要不然会引起资源泄露,有可能导致程序崩溃。 

2 调用GC并不保证GC实际执行。 
3 finalize抛出的未捕获异常只会导致该对象的finalize执行退出。 
4 用户可以自己调用对象的finalize方法,但是这种调用是正常的方法调用,和对象的销毁过程无关。 
5 JVM保证在一个对象所占用的内存被回收之前,如果它实现了finalize方法,则该方法一定会被调用。Object的默认finalize什么都不做,为了效率,GC可以认为一个什么都不做的finalize不存在。 
6 对象的finalize调用链和clone调用链一样,必须手工构造。 
如 

Java代码  收藏代码

  1. protected void finalize() throws Throwable {  
  2.     super.finalize();  
  3. }  

对象的销毁过程 
在对象的销毁过程中,按照对象的finalize的执行情况,可以分为以下几种,系统会记录对象的对应状态: 
unfinalized 没有执行finalize,系统也不准备执行。 
finalizable 可以执行finalize了,系统会在随后的某个时间执行finalize。 
finalized 该对象的finalize已经被执行了。 
GC怎么来保持对finalizable的对象的追踪呢。GC有一个Queue,叫做F-Queue,所有对象在变为finalizable的时候会加入到该Queue,然后等待GC执行它的finalize方法。 
这时我们引入了对对象的另外一种记录分类,系统可以检查到一个对象属于哪一种。 
reachable 从活动的对象引用链可以到达的对象。包括所有线程当前栈的局部变量,所有的静态变量等等。 
finalizer-reachable 除了reachable外,从F-Queue可以通过引用到达的对象。 
unreachable 其它的对象。 
来看看对象的状态转换图。 
好大,好晕,慢慢看。 
1 首先,所有的对象都是从Reachable+Unfinalized走向死亡之路的。 
2 当从当前活动集到对象不可达时,对象可以从Reachable状态变到F-Reachable或者Unreachable状态。 
3 当对象为非Reachable+Unfinalized时,GC会把它移入F-Queue,状态变为F-Reachable+Finalizable。 
4 好了,关键的来了,任何时候,GC都可以从F-Queue中拿到一个Finalizable的对象,标记它为Finalized,然后执行它的finalize方法,由于该对象在这个线程中又可达了,于是该对象变成Reachable了(并且Finalized)。而finalize方法执行时,又有可能把其它的F-Reachable的对象变为一个Reachable的,这个叫做对象再生。 
5 当一个对象在Unreachable+Unfinalized时,如果该对象使用的是默认的Object的finalize,或者虽然重写了,但是新的实现什么也不干。为了性能,GC可以把该对象之间变到Reclaimed状态直接销毁,而不用加入到F-Queue等待GC做进一步处理。 
6 从状态图看出,不管怎么折腾,任意一个对象的finalize只至多执行一次,一旦对象变为Finalized,就怎么也不会在回到F-Queue去了。当然没有机会再执行finalize了。 
7 当对象处于Unreachable+Finalized时,该对象离真正的死亡不远了。GC可以安全的回收该对象的内存了。进入Reclaimed。 
对象重生的例子 

Java代码  收藏代码

  1. class C {  
  2.     static A a;  
  3. }  
  4.   
  5. class A {  
  6.     B b;  
  7.   
  8.     public A(B b) {  
  9.         this.b = b;  
  10.     }  
  11.   
  12.       
  13.     public void finalize() {  
  14.         System.out.println("A finalize");  
  15.         C.a = this;  
  16.     }  
  17. }  
  18.   
  19. class B {  
  20.     String name;  
  21.     int age;  
  22.   
  23.     public B(String name, int age) {  
  24.         this.name = name;  
  25.         this.age = age;  
  26.     }  
  27.   
  28.       
  29.     public void finalize() {  
  30.         System.out.println("B finalize");  
  31.     }  
  32.   
  33.       
  34.     public String toString() {  
  35.         return name + " is " + age;  
  36.     }  
  37. }  
  38.   
  39. public class Main {  
  40.     public static void main(String[] args) throws Exception {  
  41.         A a = new A(new B("allen", 20));  
  42.         a = null;  
  43.   
  44.         System.gc();  
  45.         Thread.sleep(5000);  
  46.         System.out.println(C.a.b);  
  47.     }  
  48. }  

期待输出 

Java代码  收藏代码

  1. A finalize  
  2. B finalize  
  3. allen is 20  

但是有可能失败,源于GC的不确定性以及时序问题,多跑几次应该可以有成功的。详细解释见文末的参考文档。 
对象的finalize的执行顺序 
所有finalizable的对象的finalize的执行是不确定的,既不确定由哪个线程执行,也不确定执行的顺序。 
考虑以下情况就明白为什么了,实例a,b,c是一组相互循环引用的finalizable对象。 
何时及如何使用finalize 
从以上的分析得出,以下结论。 
1 最重要的,尽量不要用finalize,太复杂了,还是让系统照管比较好。可以定义其它的方法来释放非内存资源。 
2 如果用,尽量简单。 
3 如果用,避免对象再生,这个是自己给自己找麻烦。 
4 可以用来保护非内存资源被释放。即使我们定义了其它的方法来释放非内存资源,但是其它人未必会调用该方法来释放。在finalize里面可以检查一下,如果没有释放就释放好了,晚释放总比不释放好。 
5 即使对象的finalize已经运行了,不能保证该对象被销毁。要实现一些保证对象彻底被销毁时的动作,只能依赖于java.lang.ref里面的类和GC交互了。 
参考 
关于引用类型,GC,finalize的相互交互可以参考

 

转载于:https://my.oschina.net/u/3318187/blog/1633346

你可能感兴趣的文章
ACL-文件访问控制列表
查看>>
css解决div子元素margin溢出的问题
查看>>
linux内核参数注释与优化
查看>>
grep小练习
查看>>
英语文章、常用短语部分摘选集锦
查看>>
ADMT3.2域迁移之Server2003至Server2012系列(七)安装ADMT3.2
查看>>
DISPLAY环境变量的作用
查看>>
006.递归和分治思想
查看>>
org.springframework.data.mapping.PropertyReferenceException: No property xxxx found for type Xxxx
查看>>
Gson解析json数据 亲自测试可用
查看>>
我与监控宝之间的点点滴滴
查看>>
delphi 数据库显示的TDBGrid配置
查看>>
对51CTO的看法
查看>>
userenv和sys_context函数
查看>>
是否会回到起点.回忆只能是回忆
查看>>
原创数据结构算法Flash动画演示课件-Action Script(AS)脚本实现
查看>>
基于Mysql主从同步的读写分离
查看>>
BA 的岗位要求3
查看>>
基于Hyper-V3.0搭建XenDesktop7之九 部署虚拟应用之模板准备
查看>>
JS如何捆绑TypeScript声明文件
查看>>