Java内存管理,内置垃圾回收,是语言最好的成就之一。它允许开发人员创建新对象,而不用担心内存分配和释放,因为垃圾收集器会自动回收内存以供重用。这可以使用更少的样板代码实现更快的开发,同时消除内存泄漏和其他与内存相关的问题。至少在理论上。
具有讽刺意味的是,Java垃圾收集似乎工作得很好,创建和删除太多的对象。大多数内存管理问题已经得到解决,但通常是以造成严重的性能问题为代价的。使垃圾收集适应各种情况导致了一个复杂而难以优化的系统。为了围绕垃圾收集包头,首先需要了解Java虚拟机(JVM)中的内存管理是如何工作的。
垃圾收集如何真正起作用
许多人认为垃圾收集和丢弃死物。实际上,Java垃圾收集正在做相反的事情!实时对象被跟踪,其他一切都被指定为垃圾。正如你所看到的,这个根本的误解会导致很多性能问题。
让我们从堆,这是用于动态分配的内存区域开始。在大多数配置中,操作系统在程序运行时预先分配堆以由JVM管理。这有几个重要的后果:
- 对象创建速度更快,因为每个对象都不需要与操作系统进行全局同步。一个分配只是声明一部分内存数组,并向前移动偏移指针(见图2.1)。下一个分配从这个偏移量开始,并声明数组的下一个部分。
- 当一个对象不再使用的时候,垃圾回收器会回收底层的内存,然后重用它来为将来的对象分配。这意味着没有明确的删除操作并且没有内存返回给操作系统。
新的对象被简单地分配在使用堆的末尾。
可达性分析--GC Root
每个对象树必须有一个或多个根对象。只要应用程序能够到达那些根,整棵树就可以到达。但是,这些根对象何时被认为是可达的?被称为垃圾收集根的特殊对象,总是可以访问的,任何拥有垃圾收集根的对象也是可以访问的。
Java中有三种GC根源:
- 局部变量通过线程堆栈保持活动状态。这不是一个真正的对象虚拟参考,因此是不可见的。对于所有的意图和目的,局部变量是GC根。
- 活动Java线程始终被视为活动对象,因此是GC根。这对线程局部变量尤其重要。
- 静态变量由它们的类引用。这使得它们成为事实上的GC根源。
因此,一个简单的Java应用程序具有以下GC根:
- 主要方法中的局部变量
- 主线程
- 主类的静态变量
引用计数器
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。
这种计数法无法解决循环引用的问题