jvm垃圾回收(一)

来源:互联网 发布:淘宝限时打折有用吗 编辑:程序博客网 时间:2024/04/30 11:37

点击图片领取阿里云云产品幸运券

jvm中堆和栈的区别

这里所说的栈不是java.util.Stack的数据结构,而是JVM内存中的区域。堆和栈的区别可以分为以下几个方面:

  • 栈内存是用来存放局部变量和方法调用的,而堆内存是用于存储对象的,比如:Object object = new Object();这条命令会声明一个object的引用,指向新建的Object对象,其中object变量存储在栈,而新建的对象则存储在堆上;
  • 每个线程都有自己独立的栈空间,其他线程无法进行访问,也即栈是线程的私有存储空间;而堆内存对所有的线程可见,堆内存中的对象可以被所有线程访问;
  • 发生内存用尽的异常不同,栈空间溢出的话抛出的异常为java.lang.StackOverFlowError;如果堆内存溢出的话,抛出的异常为java.lang.OutOfMemoryError;
  • 栈的内存空间大小要远远小于堆内存空间,这一点显而易见,因为栈只用于存储变量,而堆用于存储对象,对象往往需要占用很大的空间。

堆内存是运行时数据区,在运行时,Java实例对象会被创建保存到堆上,当对象不被引用时,这个对象对于Java垃圾回收器来说,就变成可以回收的。
由于对象的生命周期差异很大,为了更好的实现垃圾回收的效率,堆内存分为三个区域:年轻代、年老代、永久代。

年轻代又被划分为3个区:eden,s0和s1,对象第一次被创建的时候,会存储到eden区,当eden区的可用空间不足以存放新对象的时候,这时候会触发垃圾回收的操作,MinorGC将eden区的存活对象复制到s0区,复制完毕之后,将eden区的所有对象全部清空,这时候eden区即可以为新创建的对象分配存储空间;当eden区再次空间不足时,会触发新的垃圾回收操作,这时候会把eden区和s0区的存活对象复制到s1区,如果对象的存活时间达到一定的程度,则会被复制到年老代,复制完毕之后,eden区和s0都被清空。

年老代存放生命周期较长,同时存活几率高的对象,当年轻代的存活时间达到一定程度之后,年轻代的对象会被移动到年老代。年老代的存储空间不足时,会触发full GC垃圾回收,也就是MajorGC,MajorGC的垃圾回收算法与年轻代的有所不同。

永久代存放一些应用程序启动时候加载的类,这些对象的生命周期可能对应于整个程序的运行期间,所以生命周期较年老代要更长。

垃圾回收算法

  • 引用计数法:给对象添加一个引用计数器,每当有新的引用指向该对象时,计数器值就加1;当引用失效时,计数器值就减1;当对象的引用计数为0时,就可以对其进行垃圾回收;

  • 追踪引用算法:将所有的对象以及之间的引用关系看作一棵树,选定一个对象为根节点,对所有节点进行遍历,当一个对象到根节点没有引用链相连时,则该对象不可达,该对象是不可使用的,垃圾收集器将回收其所占的内存。

MinorGC和MajorGC

HotSpot虚拟机的垃圾回收主要分为两种MinorGC和MajorGC,其中前者作用于年轻代,后者作用于年老大以及永久代,并且二者所用的垃圾回收算法也有所区别。MinorGC发生在年轻代,由于年轻代的对象生命周期较短,所以MinorGC的发生频率较高,当然执行速度也快,对于应用程序影响不大;MajorGC发生在年老代和永久代,因为这两个区的对象生命周期较长,需要进行垃圾回收的操作频率较低,另一方面,因为对象较大,进行垃圾回收的操作会比较耗时,所以应该尽量减少MajorGC的操作,以免对应用程序的运行造成不良影响。

MinorGC

MinorGC作用于年轻代,采用的是复制-删除算法。当eden区内存空间不足时,引发垃圾回收的操作,存活的对象从eden区复制到survivor区,复制完之后,将eden区所有对象清除。

MajorGC

MajorGC作用于年老代和永久代,当进行MinorGC的时候,年轻代中存活时间达到一定程度的对象会被提升到年老代。MajorGC采用的垃圾回收算法是标记-整理算法,进行垃圾回收的时候,将不再被引用的对象进行标记清除,所有的可回收对象被清除之后,将所有剩下的存活对象移动到一起,消除因为垃圾回收造成的内存碎片问题。

对年老代和永久代的垃圾回收耗时长、占用资源多,而且由于垃圾回收是一个单独的线程运行,所以长时间的垃圾回收操作会给人一种应用程序挂起的状态,所以应该尽量避免MajorGC的操作,尽量进行MinorGC的操作。在写代码的时候,可以通过尽量使用局部变量、对实例变量使用完赋值null以及对list进行clear的操作,来避免产生引用指向不再使用的对象的情况,这样可以减少MajorGC的操作。

四种不同类型的引用

Java中存在四种类型的引用:强引用、弱引用、软引用以及幽灵引用(也叫虚引用)。

通常我们说,如果一个对象被一个或者多个引用所指向的话,这个对象是不会被JVM回收的。但是,这要根据引用的类型来决定。

  • 强引用

强引用是最普遍的引用,Object o=new Object(); o就是一个强引用。如果一个对象被强引用所指向,那么垃圾回收器不会对其进行回收,即便抛出内存溢出的错误。

如果强引用的对象需要被垃圾回收器回收,我们可以显示的通过将引用赋值为null,来使得对象可以被回收,但是何时回收要取决于JVM

  • 软引用

如果一个对象没有强引用指向它,只具有软引用的话,那么该对象是否被垃圾回收器回收取决于堆内存空间的可用空间容量。如果堆内存空间足够,则垃圾回收器不会对其进行回收;如果内存空间比较紧张,那么垃圾回收器会回收这些对象的内存。

  • 弱引用

弱引用类似于软引用,区别在于:弱引用指向的对象具有更短暂的生命周期。如果一个对象只有弱引用的话,不管堆内存空间的可用空间如何,垃圾回收器都会对其进行回收。

  • 幽灵引用

幽灵引用不会决定对象的生命周期,如果一个对象仅持有虚引用,那么就和没有引用一样,这个对象随时都可以被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的情况。

触发垃圾回收

垃圾回收的操作是由Java虚拟机自动运行的,开发者却可以通过调用System.gc()或者Runtime.gc()来请求JVM启动垃圾回收。尽管可以通过这种方式来促使JVM进行垃圾回收,但是最终是由JVM基于eden区内存空间来决定的。


点击图片领取阿里云云产品幸运券