看到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的泄漏,则是因为各种原因,对象对应用已经无用,但一直被持有,一直可到达。
总结原因无外乎几方面:
- 被生命周期极长的集合类不当持有,号称是Java内存泄漏的首因。
这些集合类的生命周期通常极长,而且是一个辅助管理性质的对象,在一个业务事务运行完后,如果没有将某个业务对象主动的从中清除的话,这个集合就会吃越来
越多内存,可以用WeakReference,如WeakHashMap,使得它持有的对象不增加对象的引用数。
- Scope定义不对,这个很简单了,方法的局部变量定义成类的变量,类的静态变量等。
- 异常时没有加finally{}来释放某些资源,JDBC时代也是很普遍的事情。
- 另外一些我了解不深的原因,如: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/
浏览。
分享到:
相关推荐
JVM内存管理的介绍,编写GC友好的代码。 本材料主要关心 Sun Hotspot JVM 6的内存管理 Sun Hotspot JVM 6的GC模型 主要针对JVM6的GC模型,但也会简单介绍Java 7的G1 编写GC友好代码的一些技巧
本文将深入探讨Sun Hotspot JVM 6的内存管理与GC模型,并介绍如何编写GC友好的代码。 #### 二、Java平台基本概念 - **JVM**: Java Virtual Machine,负责执行符合Java规范的Class文件。 - **JRE**: Java Runtime ...
在MTK6739平台上,集成GC2385需要编写和调试特定的驱动代码来确保两者之间的通信和功能正常运行。这通常包括初始化配置、图像捕获、数据传输、I/O控制等多个环节。 驱动开发首先需要理解GC2385的数据手册,其中包含...
在实际编程过程中,通常会使用C语言或汇编语言编写STC15W104的驱动代码,并结合中断服务程序来处理SPI通信和定时器更新。同时,为了调试和优化,可能需要使用示波器或逻辑分析仪来观察SPI信号的正确性。 总之,STC...
3. **GC2235驱动**:GC2235的驱动程序是为这个特定摄像头传感器编写的,它负责初始化传感器,设置参数(如分辨率、帧率),以及捕获和处理图像数据。驱动可能包括了ISP(Image Signal Processor)配置,用于对原始...
其次,`TFT_LCD.c`和`TFT_LCD.H`是C语言编写的源代码文件,它们是针对特定TFT LCD屏幕的驱动程序实现。`TFT_LCD.c`通常包含了驱动程序的主要函数实现,比如初始化、像素设置、颜色管理和扫描控制等。而`TFT_LCD.H`是...
编写gc2145驱动代码时,开发者需要遵循操作系统提供的驱动开发框架,例如在Windows系统中,可能使用Windows Driver Framework (WDF),而在Linux系统中,则可能使用Kernel Driver Development Kit (KDK) 或直接编写...
压缩包中的文件"SENSOR_GC1054.c"很可能是C语言编写的源代码,它可能包含了驱动程序或者示例代码,用于演示如何配置和操作GC1054传感器。C语言是嵌入式系统和硬件驱动程序开发的常用语言,因为它效率高且可以直接...
1. **调试和优化驱动代码**:确保STM32C8T6与GC9106之间的通信无误,优化图像显示速度和效果。 2. **界面设计**:使用`.uvgui`文件编辑器创建或调整用户界面布局,包括图标、文字和其他元素。 3. **背光调节**:通过...
1. **源代码**:Go语言编写的5G核心网络组件,包括AMF、SMF、UPF等的实现。 2. **配置文件**:用于设置各个组件的参数,如网络地址、接口配置、安全设置等。 3. **虚拟机镜像或脚本**:可能包含预装好free5gc环境的...
对于SPI接口,开发者需要编写代码来设置时钟频率、极性、相位等参数,以及定义数据传输的命令序列。对于GC6153模组的240*320分辨率,驱动需要处理像素数据的读写操作,确保图像正确地显示在屏幕上。此外,还需要考虑...
在编写驱动时,需要实现V4L2的回调函数,如`video_device_init`、`v4l2_open`、`v4l2_mmap`等,以便系统能够正确地初始化、打开和映射设备。 为了测试驱动,通常会使用一些工具如`v4l2-ctl`来查询和设置摄像头参数...
【描述】中的"mt6737平台上的摄像头驱动代码"表明这是专门为采用联发科(Mediatek) MT6737芯片组的设备编写的驱动程序。MT6735是联发科推出的一款四核64位ARM Cortex-A53处理器,常用于入门级和中端智能手机和平板...
《GC9503V-DS IC规格书》是一份重要的技术文档,主要面向嵌入式开发领域的工程师,尤其在进行点屏操作或编写显示驱动程序时,此规格书是不可或缺的参考资料。嵌入式系统通常涉及到硬件和软件的紧密集成,其中显示...
在代码编写中,可能会使用到如Arduino或MicroPython等为ESP32准备的框架。这些框架提供了丰富的库函数,简化了硬件驱动的编程工作。例如,可能需要使用`spi.begin()`来初始化SPI接口,`spi.write()`来发送数据,以及...
在数控机床领域,G代码是控制CNC(计算机数控)机床运行的一种编程语言,用于指示机器执行各种操作,如直线切割、圆弧运动等。本教程以“数控机床G代码解析demo”为例,深入探讨如何从G代码程序中提取关键数据,如坐...
在数据手册中,你可以找到每个寄存器的地址、位宽、默认值和功能说明,这对于编写驱动程序和调试系统至关重要。例如,手册会指出如何通过特定寄存器设置像素大小,调整图像质量,或者启用或禁用某些功能,如自动曝光...
1. **驱动开发**:为GC0308摄像头模组编写或适配驱动程序,使其能够正确地与MTK6573或MTK6516平台通信,包括初始化、数据传输和控制命令的执行。 2. **性能优化**:根据硬件特性,优化摄像头的帧率、图像质量等,以...
在“gc5035_mipi_raw.rar”压缩包中,包含的是针对GC5035的驱动源代码,适用于Android Q(即Android 10.0)系统。驱动代码通常分为用户空间和内核空间两部分,用户空间部分主要处理应用程序的调用,而内核空间部分则...
.NET中的GC(Garbage Collection)是自动内存管理的关键部分,它负责识别并回收不再使用的对象,从而避免内存泄漏。在C#中,由于语言的特性...了解并合理运用GC的工作原理,有助于编写出高效且资源友好的.NET应用程序。