周一,早高峰。
一段考验一个人耐力、智力、开车技术以及脾气的路。
我把车开进了一个没有红绿灯的丁字路口,然后就没有然后了。
来自三个方向的大车小车开始在不大的一块空间里开始互相斗智斗勇,但是最终的结果永远是伤敌一千,自损八百。 这种崩溃的心情应该很多人都体会过,特别是本来不用迟到,但是堵车却能让你迟到上好几个小时。
在所有人都陷入绝望的时候,交警同志终于到达现场,开始疏散车辆。那些给我贴罚单的“讨厌”的人,一下子变成了无比高大的存在。 此时已经是快要九点半了,快要两个小时的时间,我竟然只挪动了两米不到。
一刹那间,我终于体会到什么是垃圾回收机制,你能体会吗?使用java进行编程,但是从来没有去思考过这个问题的小伙伴们? 这个巨大的丁字路口,可以理解为内存,可以允许有限的车辆在里面通行。 当车辆不停地始驶入,就像是无数的线程都要放到内存中,需要被处理。终于,内存爆了。接下来的一幕就是内存溢出,停止工作。 那些已经被不会被使用的垃圾线程依然停留在内存当中,就好像横在我眼前的这个大卡车。(当然对我来说,除了我之外的所有的车辆都是垃圾,应该被回收掉) 这个时候,内心里有一个声音:如果有人能把它们带走就好了。 于是,交警来了,他就是java中的垃圾回收机制。他以极快的速度把所有停滞在内存当中的废弃线程快速的清除出去。 于是,终于,一切又开始高速的运转开来。
在java之前众多的编程语言,都是通过编程人员自己来清除内存中已经被废弃掉的无用线程或者对象,这样就使得程序员的精力过多的消耗在了系统上。如果系统自身可以完成这部分工作,程序员就能够更多的去关注业务本身而不是系统效率。 这样做的好处是解放程序的大脑,但是坏处也是显而易见的。 那就是系统提供的垃圾回收机制无法做到像人工操作一样,在第一时间就知道代码中的对象已经使用完毕,而立即清除。系统需要固定的时间和固定的方式来做这件事情。这也就是众所周知的Android系统总是在开始的非常流畅,但是在使用一段时间之后就变得缓慢异常。好在通过java的不断升级,java语言的维护团队对效率做了很大的优化。 但是,无论多么优秀的垃圾回收机制,依然无法像人一样第一时间对无用的内存占用进行清除。一件工具很难做到尽善尽美,就像hibernate一样,降低了程序员对复杂SQL书写的要求,但是却减慢了效率。
在开始详细介绍垃圾回收机制的工作原理之前,有一个小小的思维拓展。
我记得刚开始接触到java的时候,老师讲对象在内存中的存储时,总是告诉我,引用存储在栈中,而对象是存储在堆中。老实说,即使是几年之后,如此抽象的讲解依然让我无法理解java的底层工作到底是怎样的? 而所谓的栈,堆又到底是什么,根本没有一个解释能让人直观的理解程序在内存中到底是怎样进行工作的。
那么说一些我自己的理解。 众所周知,计算机所有的工作原理是建立在二进制上。所谓二进制课本上告诉我们是由1,0组成的一种进制,而原因是机器只能识别二进制。好吧,关于二进制其实也是云雾缭绕的存在,我现在特别不理解在课堂上老师怎么能如此官面的解释二进制。
我们输入指令,计算机要做的事情有两个,第一是识别这道指令,第二是执行这道指令。那么是否可想想象二进制的存储方式就是一种信息的表达,就是把1,0写在一个数组当中,而计算机通过阅读这一串1,0组成的机器码,了解了指令的内容。然后它处理完这道指令之后,又以1,0的方式反馈给我们,这种二进制的信息存储最终被翻译成了我们能够识别的文字,音乐,图像等等。
说到最后,二级制就是一种信息的存储方式罢了。
那么所谓的栈,是否可以理解为java虚拟机将内存中的某一块区域指定为栈,这块区域是通过1,0 的方式存储指令。这种指定本身也不是固定,也许下一次虚拟机指定了另外的地方。如此是否可以更好地理解所谓的栈和堆呢。不过是强行划分的内存区域,并且在这个区域中是按照java虚拟机指定的规则进行数据的存储和阅读的。
不知这样的理解是否正确,还请指正。
1.stop-the-world
让这个世界都停止,让时间停止。这个程序员一定是个诗人,才能给这段程序起到如此可爱的一个名字。 Stop-the-world意味着 JVM 因为要执行GC而停止了应用程序的执行。当Stop-the-world发生时,除了GC所需的线程以外,所有线程都处于等待状态,直到GC任务完成。GC优化很多时候就是指减少Stop-the-world发生的时间。
2.垃圾回收机制是怎样辨别哪些对象是需要进行回收的?
在此时我们大体梳理一下整个过程。 在Java中,开发人员无法直接在程序代码中清理内存,而是由垃圾回收器自动寻找不必要的垃圾对象,并且清理掉他们。垃圾回收器会在下面两种假设(hypotheses)成立的情况下被创建(称之为假设不如改为推测(suppositions)或者前提(preconditions))。 大多数对象会很快变得不可达 只有很少的老年对象(创建时间较长的对象)指向新生对象的引用 这些假设我们称之为弱年代假设( weak generational hypothesis)。为了强化这一假设,虚拟机将其物理上划分为两个–新生代(young generation)和老年代(old generation)。 新生代(Young generation): 绝大多数最新被创建的对象会被分配到这里,由于大部分对象在创建后会很快变得不可到达,所以很多对象被创建在新生代,然后消失。对象从这个区域消失的过程我们称之为”minor GC“。
老年代(Old generation):对象没有变得不可达,并且从新生代中存活下来,会被拷贝到这里。其所占用的空间要比新生代多。也正由于其相对较大的空间,发生在老年代上的GC要比新生代少得多。对象从老年代中消失的过程,我们称之为”major GC“(或者”full GC“)
新生代的构成
为了更好地理解GC,我们现在来学习新生代,新生代是用来保存那些第一次被创建的对象,他可以被分为三个空间
一个伊甸园空间(Eden ) 两个幸存者空间(Survivor )
一共有三个空间,其中包含两个幸存者空间。每个空间的执行顺序如下:
绝大多数刚刚被创建的对象会存放在伊甸园空间。 在伊甸园空间执行了第一次GC之后,存活的对象被移动到其中一个幸存者空间。 此后,在伊甸园空间执行GC之后,存活的对象会被堆积在同一个幸存者空间。 当一个幸存者空间饱和,还在存活的对象会被移动到另一个幸存者空间。之后会清空已经饱和的那个幸存者空间。 在以上的步骤中重复几次依然存活的对象,就会被移动到老年代。
如果你仔细观察这些步骤就会发现,其中一个幸存者空间必须保持是空的。如果两个幸存者空间都有数据,或者两个空间都是空的,那一定标志着你的系统出现了某种错误。
老年代GC处理机制
老年代空间的GC事件基本上是在空间已满时发生,执行的过程根据GC类型不同而不同。
到此为止,是否可以更加直观的理解所谓垃圾回收机制.
这是我对java语言的一些简单理解,原创作品,欢迎关注同名微信订阅号:程序员与工匠。
欢迎各位大大扫描下方二维进行关注。