`

编写对GC友好,又不泄漏的代码

    博客分类:
  • Java
阅读更多

编写对GC友好,又不泄漏的代码

    作者:江南白衣

    看到JavaOne2007上有篇《Garbage-Collection-Friendly Programming》的68页PPT,心想都2007了还谈这个基本问题,一定总结得很全面了才好意思站出来讲吧。    

GC的基础概念见上篇:JDK5.0垃圾收集优化之--Don't Pause  

1.使用更多生命周期短的、小的、不改变指向(immutable)的对象,编写清晰的代码。

    出于懒惰也好,朴素的节俭意识也好,我们都习惯对一个变量重用再重用。但是....

  • Java的垃圾收集器喜欢短生命周期的对象,对象如果在新生代内,在垃圾收集发生前就死掉了,垃圾收集器就什么都不用做了。
  • 现代JVM构建一个新对象只需要10个本地CPU指令,并不弱于C/C++。 (但垃圾收集没有压缩算法时会稍慢,更频繁的New对象也导致更频繁的GC)。
  • 大对象的分配效率更低,而且对非压缩算法的垃圾收集器,更容易造成碎片。
  • 对象重用增加了代码的复杂度,降低了可读性。

    所以有标题的呼吁,比如不要害怕为中间结果分配小对象。但编程习惯的改变也不是一朝一夕的事情。

2.将用完的对象设为NULL其实没什么作用。

    貌似很酷的把对象主动设为Null 的"好习惯"其实没什么用,JIT Compiler会自动分析local变量的生命周期。
    只有一个例外情况,就是String[1024] foo 这种赤裸裸的数组,你需要主动的foo[100]=null释放第100号元素,所以最好还是直接用ArrayList这些标准库算了。

3.避免显式GC--System.gc()。

    大家都知道System.gc()不好,full-gc浪费巨大,gc的时机把握不一定对等等,甚至有-XX:+DisableExplicitGC的JVM参数来禁止它。

     哈哈,但我还不会用System.gc()呢,不怕不怕。真的不怕吗?

  • 先用FindBugs 查一下所用到的全部第三方类库吧...
  • 至少RMI 就会老实不客气的执行System.gc()来实现分布式GC算法。但我也不会用RMI啊。那EJB呢,EJB可是建在RMI上的....

    如果无可避免,用-Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 (单位为微妙) 增大大GC的间隔(原默认值为1分钟),-XX:+ExplicitGCInvokesConcurrent 让System.gc() 也CMS并发执行。

4.继续千夫所指的finalize()

    大家也都知道finalize()不好,分配代价昂贵,释放代价更昂贵(要多走一个循环,而且他们死得慢,和他们相关联的对象也跟着死得慢了),又不确定 能否被调用(JVM开始关闭时,就不会再进行垃圾收集),又不确定何时被调用(GC时间不定,即使system.gc()也只是提醒而不是强迫GC,又不 确定以什么样的顺序调用,所以finalize不是C++的析构函数,也不像C++的析构函数。

   我们都知道啊,所以我从来都没使用。都是在显式的维护那些外部资源,比如在finally{}里释放。

5.WeakReference/SoftReference

   这是个平时不怎么会搭理,偶然知道了又觉得有用的Java特征。大家都知道Java里所有对象除int等基本类型外,都是Pass by Reference的指针,实例只要被一个对象连着,就不会被收集。
    而WeakReference就是真正意义上的C++指针,只是单纯的指向一个对象,而不会影响对象的引用计数。
    而SoftReference更特别,在内存足够时,对象会因为SoftReference的存在而不被收集,但内存不足时,对象就还是会被收集,怎么看都是做简单缓存的料子。代码如下:

   Foo foo = new Foo();
   SoftReference sr
= new SoftReference(foo);
   Foo bar
=   sr.get();

如果foo已被垃圾收集,sr.get()会返回Null;

另外还有一个ReferenceQueue的机制,使得对象被回收时能获得通知,比finalize()完全不知道GC何时会执行要聪明的多。

   ReferenceQueue rq = new ReferenceQueue();
   ref
= new WeakReference(foo, rq);
   WeakReference cleaned
= rq.pool();

cleaned就是刚刚被GC掉的WeakReference。

6.内存泄漏

   java 不是有垃圾收集器了吗?怎么还泄漏啊,唬我啊??
   嗯,此泄漏非比泄漏。C/C++的泄漏,是对象已不可到达,而内存又没有回收,真正的内存黑洞。
   而Java的泄漏,则是因为各种原因,对象对应用已经无用,但一直被持有,一直可到达。
   总结原因无外乎几方面:

  1. 被生命周期极长的集合类不当持有,号称是Java内存泄漏的首因。
    这些集合类的生命周期通常极长,而且是一个辅助管理性质的对象,在一个业务事务运行完后,如果没有将某个业务对象主动的从中清除的话,这个集合就会吃越来越多内存,可以用WeakReference,如WeakHashMap,使得它持有的对象不增加对象的引用数。
  2. Scope定义不对,这个很简单了,方法的局部变量定义成类的变量,类的静态变量等。
  3. 异常时没有加finally{}来释放某些资源,JDBC时代也是很普遍的事情。
  4. 另外一些我了解不深的原因,如:Swing里的Listener没有显式remove;内部类持有外部对象的隐式引用;Finalizers造成关联对象没有被及时清空等。

内存泄漏的检测

有不少工具辅助做这个事情的,如果手上一个工具也没有,可以用JDK自带的小工具:

  • 看看谁占满了Heap?
    用JDK6的jmap可以显示运行程序中对象的类型,个数与所占的大小
    先用jps 找到进程号,然后jmap -histo pid 显示或 jmap -dump:file=heap_file_name pid 导出heap文件
  • 为什么这些对象仍然可以到达?
    用jhat(Java Heap Analysis Tool) 分析刚才导出的heap文件。
    先jhat heap_file_name,然后打开浏览器http://localhost:7000/ 浏览。

分享到:
评论

相关推荐

    优秀Java程序员必须了解的GC工作原理

    优化GC性能的关键在于理解并合理配置JVM参数,以及编写内存友好的代码。以下是一些提升Java程序性能的建议: 1. 使用适当的对象池技术,减少频繁创建和销毁对象的开销。 2. 避免长生命周期的对象持有短生命周期的...

    关于JAVA内存泄漏问题注意事项

    Java内存泄漏问题是一个重要的主题,尤其对于大型的J2EE应用程序而言,理解并避免内存泄漏至关重要。...理解这些注意事项并将其应用于实践中,可以帮助开发者编写出更加高效且内存友好的Java代码。

    C语言的科学与艺术_源代码使用的库

    6. **gcalloc.h**: "gcalloc"可能指的是“garbage collected allocation”,即垃圾收集分配,这在C语言中是不常见的,因为C语言本身并不支持自动垃圾回收。这个库可能提供了动态内存分配的管理机制,以避免内存泄漏...

    学习文档MQ+Redis

    本学习文档包含的资料如"JVM运行时数据区.pdf"和"JVM内存管理_GC模型_编写GC友好的代码.ppt"将详细介绍JVM的工作原理和优化技巧,而"RocketMQ.rar"和"redis.zip"则提供了关于RocketMQ和Redis的实践教程,帮助你深入...

    Java Performance

    资料如《JDK5.0 垃圾收集优化之 Don't Pause》和《编写对GC友好的代码》提供了宝贵的指导,强调了避免程序暂停和防止内存泄漏的重要性。GC日志的打印是调优过程中的基础,通过-XX:+PrintGCDetails、-XX:+...

    java内存管理的原理.docx

    了解Java内存管理原理对开发人员来说至关重要,它可以帮助编写更高效、内存友好的代码。例如,避免过多的小对象创建,使用StringBuffer替代StringBuilder以减少内存分配,以及合理地配置JVM参数,都能有效提升应用...

    笔记,2、垃圾回收器和内存分配策略1

    3. 编程指导:了解GC可以帮助开发者编写出更高效、内存友好的代码,减少内存泄漏和性能瓶颈。 二、判断对象存活的算法 1. 引用计数法:每个对象都有一个计数器,每当有一个引用指向它时,计数器加1,引用消失时减1...

    JDK 1.8 Windows x86

    综上所述,JDK 1.8 for Windows x86是Java开发的重要里程碑,它带来了许多对开发者友好的新特性,提升了开发效率和代码质量,同时也优化了运行时性能。对于32位Windows系统上的Java应用开发者来说,这是一个不可或缺...

    Php引用计时器和垃圾回收机制.pdf

    在PHP编程语言中,引用计数器和垃圾回收机制是两个关键的概念,它们与变量的生命周期管理和内存管理紧密相关。... ...例如,你可以将字符串赋值给一个...理解这些机制的工作原理,有助于编写出更加高效、内存友好的代码。

    强,软,弱,虚引用非常通俗易懂的代码理解

    Java中的引用类型是垃圾收集机制的重要组成部分,它们允许开发者对对象的生命周期进行更精细的控制。本文主要讨论四种引用类型:强引用、软引用、弱...理解这些引用类型有助于编写更加高效且内存友好的Java应用程序。

    c#内存检测工具-----

    同时,理解C#内存管理机制,结合实际的检测结果,有助于编写更高效、资源友好的代码。对于大型或复杂项目,定期进行内存分析和优化是必要的,以确保应用程序在长时间运行后仍能保持良好的性能。

    Flash强制垃圾内存回收测试

    在IT行业中,内存管理是程序优化的关键环节,尤其是在性能敏感的...理解并掌握这些知识,可以帮助开发者编写出更加高效、内存友好的Flash应用程序。同时,通过工具进行内存分析和性能测试也是优化过程中的重要步骤。

    Android应用深度优化-正式

    - **适时使用ViewStub**:对不常用的View使用ViewStub进行延迟加载,减少初始化时的资源占用。 6. **代码结构与设计模式** - **遵循MVC/MVP/MVVM**:合理的架构设计能够提高代码可读性和可维护性,减少耦合。 - ...

    分配内存 code

    通过深入理解C#的内存管理和垃圾收集机制,开发者能够编写出更加高效、内存友好的代码,避免不必要的性能瓶颈。在实际项目中,对LOH的优化往往能带来显著的性能提升,特别是在内存敏感的应用场景下。

    C# Winform windows运行内存释放

    开发者可能需要手动触发GC,如使用`GC.Collect()`方法,但这种方法并不推荐,因为GC的优化策略可能会比手动操作更有效。 2. **进程管理**:通过`System.Diagnostics.Process`类,可以列出所有运行的进程,并获取每...

    java的时间空间…………

    在实际开发中,结合`java.time`包提供的日期时间API,我们可以编写出高效且易于维护的日期时间处理代码;同时,通过理解Java内存管理机制和垃圾收集原理,可以有效地避免内存泄漏,提升程序性能。这些都是Java开发者...

    数据结构JavaScript中的内存使用

    在JavaScript中,理解数据结构与内存使用是优化代码性能的...理解JavaScript中的数据结构和内存使用是提升代码性能的基础,通过优化变量使用、正确管理内存以及合理利用数据结构,可以编写出更加高效、资源友好的程序。

    Effective C# 中文版改善C#程序的50种方法

    11. **理解垃圾收集(Garbage Collection, GC)**:C#中的垃圾收集自动管理内存,但开发者仍需理解其工作原理,避免内存泄漏。 12. **使用using语句**:using语句用于确保对象被正确释放,特别适用于实现了...

Global site tag (gtag.js) - Google Analytics