在Java中,内存管理是通过自动垃圾回收(Garbage Collector,GC)来进行的。JVM会自动跟踪内存中的对象:如果一个对象没有更多的引用,它将变为不可达,并可以被删除。
GC由不同的阶段组成,例如标记(Mark)、清除(Sweep)和有时的压缩(Compact)。在标记阶段,能够从根(GC roots)访问到的对象被标记为活的。随后开始清除:未使用的对象被删除。有时会执行压缩——内存碎片整理。
GC有不同的类型:Serial、Parallel、CMS、G1。选择它们时应根据应用的负载类型。
List<byte[]> dataList = new ArrayList<>(); while(true) { dataList.add(new byte[1024*1024]); // 对象保持可达 }
在这个例子中,对象永远不会变为不可达,GC不会删除它们,结果会出现OutOfMemoryError。
如果一个对象没有更多的引用,什么时候可以保证调用它的终结器(finalize())?
回答: 根本无法保证调用finalize()方法!它可能从未被调用,或者有延迟。不能依赖finalize()来释放资源。
@Override protected void finalize() throws Throwable { // 可能不会被执行! }
故事
在一个大型电子商务系统中,开发人员希望通过
finalize()方法自动清理临时文件,但由于这个方法被调用的频率极低,缓存被临时文件填满,导致磁盘不足和停机。
故事
在高负载的REST API中,偶然保存了对大型对象的内部引用缓存,妨碍了内存的释放。应用程序因OutOfMemoryError崩溃后,分析显示缓存方法存在错误。
故事
终结器被用于释放网络连接,以为这是“可靠的方法”。这导致连接在系统中悬挂不定的时间,造成阻塞和连接池耗尽。