Java养成什么样的编程习惯可以有利于GC呢?
善用 weakrefrence(WeakHashMap)和 softrefrence;当对象的强引用都不在以后,如果HashMap 或者 ArrayList 里对它是弱引用,被引用的对象会在下次GC时被回收 关于 object pooling,虽然是很过时的技术,创建小对象的开销也越来越小(至少归功于TLAB和堆空间的分区),维护一个 pool 提供各种接口可能还开销更大,但对于数据库连接对象的创建、线程的创建,object pooling都还是管用的,手动置空,这个只在个别情况下有意义,大家都知道了Immutability的问题,多次使用不可变的对象不一定不好。当在一个容器里引用另一个对象的时候,如果要替换引用的对象,要么在原容器里替换引用(容器可变),要么创建新的容器(容器不可变)。重用容器似乎更高效,但是请注意,如果容器对象已经在老年区,重用就会引入老对象对新对象的引用,复杂化了GC操作;而创建新的容器则不会有这个问题。
因为各个JVM甚至同一个JVM里的各个GC实现都会有不同的特点。 但通用的、通常管用的建议,其实很简单: 写简单直观的代码,不要玩花招。过分设计、过多的封装/抽象层,常常会让GC很难受(导致需要处理的对象增多)。 要理解:GC是伙伴,不是仆人。在保持代码结构良好、直观易懂的前提下,减少没必要的对象分配总是好的。不要调用System.gc() <- 可能影响GC的统计数据和未来决策 不要随意使用“对象池” <- 为了优化GC而使用对象池常常是非常有害的。为了别的有用的目的,例如说持有初始化开销高的资源而使用对象池,这才是通常可取的场景。通常不用关心对局部变量置null <- 开头的传送门有详细讲解 小心使用ThreadLocal,特别是当跟线程池搭配使用的时候 <- 如果用线程池来跑任务,而这些任务向ThreadLocal写入了数据,那么应该注意在任务完成时清理ThreadLocal,不然容易泄漏 如果使用堆外内存来实现Java对象的缓存,而且在堆外内存里存的是序列化后的Java对象的话,要小心使用时的反序列化开销及其伴随的频繁创建对象的开销。如果程序里有使用NIO,要关注DirectByteBuffer的使用状况;例如说如果禁用了System.gc()并且程序调优过使得GC频率非常低的话,死掉的DirectByteBuffer可能会得不到及时的释放。
这样用强引用来持有一堆对象的话,首先这个pool所指向的数组就肯定会长命,在分代式GC里就会晋升到old gen;而在做young GC时,old gen到young gen的跨代引用是根集合的一部分,如果会时不时有新对象被加入到pool中,这个数组就很可能要在young GC时整个被扫描,拖慢young GC的速度。而如果用某种弱引用来实现对象池,弱引用处理自身也是会增加GC开销的,用得不好一样得不偿失。一个“时不时就有新对象”的pool不是合格的pool。用奇怪的实现来指摘pool的一般使用没什么意思。再者,使用pool并不一定是为了gc,有可能是因为该资源初始化特别耗时或者干脆就是需要长期持有。