Java内存管理——深入Java虚拟机(六)

来源:互联网 发布:java中service的作用 编辑:程序博客网 时间:2024/06/05 14:20

前言

 真的特别感谢这么坚持的自己,早上去面了趋势,面对以C++为主的公司,依然顺利面完二面,早上一面125个人,留下二面的25个,据说确认通过的只有4个,在旁边偷偷听到的,本来说今晚发结果的,还在等。
 为什么说特别感谢坚持的自己,因为在前面操作系统,Linux,window底层实现完全一脸懵逼以后,面试差点绝望了,但是,他最后给了我一次机会,让我解释下Java虚拟机,而博主滔滔不绝的给他说完以后,他说了这句话,你对Java虚拟机了解的很透彻啊,为什么你需要去了解这些,我说以后写代码的时候自己知道怎么处理对象优化程序,出错以后,知道怎么去差错,解决问题。面试官说你学Java,学的还是比较深的,最然在其他方面我不占优势,但是在我深入的这门语言里,我尽可能做到最好,所以我说,感谢我这些时间的坚持。也感谢各位看客们的耐心与坚持,每天一点点,用到时候,你一定感谢现在的分分秒秒。

正文
垃圾收集器与内存分配策
1.概述
 垃圾收集器并不是伴随着Java的诞生而出现的,60年代的MIT的Lisp是第一门真正使用的内存动态分配和垃圾收集技术的语言。
 为什么要去了解?
 当需要排查各种内存溢出、 内存泄漏问题时,当垃圾收集成为系统达到更高并发量的瓶颈时,我们就需要对这些“自动化”的技术实施必要的监控和调节。
 介绍了Java内存运行时区域的各个部分,其中程序计数器、 虚拟机栈、 本地方法栈3个区域随线程而生,随线程而灭;栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作。 每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的,因此这几个区域的内存分配和回收都具备确定性,在这几个区域内就不需要过多考虑回收的问题,因为方法结束或者线程结束时,内存自然就跟随着回收了。 而Java堆和方法区则不一样,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,我们只有在程序处于运行期间时才能知道会创建哪些对象,这部分内存的分配和回收都是动态的,垃圾收集器所关注的是这部分内存。下面是重点,前面跟面试官分析完,面试官听得是一愣愣的。其实有些面试官并不一定了解这么深。

2.对象回收

 我们需要收集的已经步子啊使用的对象,而不是依然存活的对象,所以我们关注点在于对象是否依然存活?怎么去判定对象是存活还是死亡呢,下面介绍两个比较常见的用法。
1.引用计数法
 这个方法不是现在虚拟机采用的,但是还是有必要说一下,为什么不用后面说。我们给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。 引用计数算法(Reference Counting)的实现简单,判定效率也很高,在大部分情况下它都是一个不错的算法,也有一些比较著名的应用案例,例如微软公司的COM(Component Object Model)技术、 使用ActionScript 3的FlashPlayer、 Python语言和在游戏脚本领域被广泛应用的Squirrel中都使用了引用计数算法进行内存管理。
为什么Java虚拟机没有采用呢,因为对象存在一种关系,相互引用,然后其他对象却不在引用他们,相当于孤立了两个他们,其实他们已经算废弃的对象了,但是因为引用计数依然为1,所以,没办法回收,这就造成了内存泄漏。
2.可达性分析
 在主流的商用程序语言(Java、 C#,甚至包括前面提到的古老的Lisp)的主流实现中,都是称通过可达性分析(Reachability Analysis)来判定对象是否存活的。 这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。 如图所示,对象object 5、 object 6、 object 7虽然互相有关联,但是它们到GC Roots是不可达的,所以它们将会被判定为是可回收的对象。

图片说明

3.引用

 一个对象在这种定义下只有被引用或者没有被引用两种状态,对于如何描述一些“食之无味,弃之可惜”的对象就显得无能为力。 我们希望能描述这样一类对象:当内存空间还足够时,则能保留在内存之中;如果内存空间在进行垃圾收集后还是非常紧张,则可以抛弃这些对象。 很多系统的缓存功能都符合这样的应用场景。
在JDK 1.2之后,Java对引用的概念进行了扩充,将引用分为强引用(StrongReference)、 软引用(Soft Reference)、 弱引用(Weak Reference)、 虚引用(PhantomReference)4种,这4种引用强度依次逐渐减弱。

4.回收方法区

 很多人认为方法区(或者HotSpot虚拟机中的永久代)是没有垃圾收集的,Java虚拟机规范中确实说过可以不要求虚拟机在方法区实现垃圾收集,而且在方法区中进行垃圾收集的“性价比”一般比较低:在堆中,尤其是在新生代中,常规应用进行一次垃圾收集一般可以回收70%~95%的空间,而永久代的垃圾收集效率远低于此。
永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类。 回收废弃常量与回收Java堆中的对象非常类似。 以常量池中字面量的回收为例,假如一个字符串“abc”已经进入了常量池中,但是当前系统没有任何一个String对象是叫做“abc”的,换句话说,就是没有任何String对象引用常量池中的“abc”常量,也没有其他地方引用了这个字面量,如果这时发生内存回收,而且必要的话,这个“abc”常量就会被系统清理出常量池。 常量池中的其他类(接口)、 方法、 字段的符号引用也与此类似。
判定一个常量是否是“废弃常量”比较简单,而要判定一个类是否是“无用的类”的条件则相对苛刻许多。 类需要同时满足下面3个条件才能算是“无用的类”:
 该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例。
 加载该类的ClassLoader已经被回收。
 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
 下节,垃圾收集算法。希望大家关注下,让作者更有动力,谢谢啦,点关注哦!

原创粉丝点击