Java虚拟机,将字节码加载到内存中之后,就是无限的执行,前面有过一张图,类比了做菜和Java的一个大致运行逻辑。
Java不需要手动释放内存,因为有自动的垃圾回收机制,也就是上图中清理的过程,这个过程很好理解。
当你做饭结束之后,随之而来的就是收拾锅碗瓢盆,这就是清理。
自动内存管理
垃圾回收机制,通俗的说就是自动的管理内存,主要就涉及到内存的使用过程中产生垃圾,产生了垃圾之后需要标记确认垃圾,然后对垃圾进行清理。
清理的目标
类加载,会将class中的内容信息加载到内存中,那么使用完之后,就是找到无用的垃圾,将他清理
简略图如下
对于内存区域中具体点就是这些虚拟机规范中存在内存溢出(OOM)可能的区域
内存分配
内存分配通常是这样两种形式:
1.假设Java堆中内存是绝对规整的。
所有被使用过的内存都被放在一边, 空闲的内存被放在另一边, 中间放着一个指针作为分界点的指示器, 那所分配内存就仅仅是把那个指针向空闲空间方向挪动一段与对象大小相等的距离, 这种分配方式称为“指针碰撞”(Bump ThePointer)
2.如果Java堆中的内存并不是规整的, 已被使用的内存和空闲的内存相互交错在一起。
没有办法简单地进行指针碰撞了, 虚拟机就必须维护一个列表, 记录上哪些内存块是可用的, 在分配的时候从列表中找到一块足够大的空间划分给对象实例, 并更新列表上的记录, 这种分配方式称为“空闲列表”(Free List) 。
选择哪种分配方式由Java堆是否规整决定, 而Java堆是否规整又由所采用的垃圾收集器是否带有空间压缩整理(Compact) 的能力决定
对象定位
reference 是指向对象的引用。
就是根据reference 指向具体的对象地址,句柄还是直接指针,指的是接下来的形式。
如果reference指向的是句柄,然后通过句柄获取类型和数据,如下图一所示,不管是访问数据还是类型,都需要进一步的跳转。
如果reference指向的是直接指针,此时已经获取到了对象的实例数据。如下图二所示。
对比:
具体的形式,依赖虚拟机的实现,Hotspot使用直接指针。
对象头信息
对象头里的信息是与对象自身定义的数据无关的额外存储成本, 考虑到虚拟机的空间效率, Mark Word被设计成一个有着动态定义的数据结构, 以便在极小的空间内存储尽量多的数据, 根据对象的状态复用自己的存储空间。
对象头的设立逻辑其实很简单,很多系统中都有类似的应用,简单说就是与具体数据无关的,用来方便给你处理数据或者用来处理数据的一些必备的额外数据。
这些额外的数据,对于虚拟机的运行、锁、垃圾清理等都有用。
清理目标的确认
确定清理目标,首先要明确的知道,他到底是不是不再使用,是不是已经成为了垃圾。
根据内存的分配,可以知道相关对象的reference,也就是知道这些对象的引用信息。
通常方法就是引用计数以及可达性分析。
对于java中的主流的都是使用可达性分析。
关于引用
无论是通过引用计数算法判断对象的引用数量, 还是通过可达性分析算法判断对象是否引用链可达,都必须借助于“引用”。
引用又分为四类:
强引用(Strongly Re-ference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)。
finalize()方法
对象在进行可达性分析后发现没有与GC Roots相连接的引用链, 那它将会被第一次标记, 随后进行一次筛选, 筛选的条件是此对象是否有必要执行finalize()方法。
假如对象没有覆盖finalize()方法, 或者finalize()方法已经被虚拟机调用过, 那么虚拟机将这两种情况都视为“没有必要执行”,那么下一次的时候,就会被清理了。
实践中直接无视这个方法就好了,存在各种潜在的风险与问题。
垃圾收集理论
分代的理论逻辑,可以认为是实践数据,对于程序的运行规律的一种总结。
比如绝大多数变量其实都是方法内的局部变量,必然朝生夕死
比如类的某些常量,可能会伴随程序运行的一生
这个也是垃圾收集器,分代进行处理的一个理论依据。
随着垃圾收集技术的演进,现在最新出现的也都开始尝试不分代进行处理。
垃圾收集算法
三种核心算法如下,是垃圾收集器的基础理论:
下图摘取自《深入理解Java虚拟机》周志明
可以比较直观的看到执行逻辑
这些垃圾收集算法是理论依据,指导了虚拟机的具体实现(换一个角度说虚拟机的实现,必然并不是照搬直接按照理论来开发的,会有很多细节的不同)
转载务必注明出处:程序员潇然,疯狂的字节X,https://crazybytex.com/thread-213-1-1.html