jvm gc策略 参数设置

java jvm内存管理/gc策略/参数设置

在C++中,我们知道创建出的对象是需要手动去delete掉的。
我们java程序运行在jvm中,jvm可以帮我们“自动”回收了我们不需要的对象,对我们来说是十分方便的。
jvm回收的是垃圾,垃圾就是我们程序中已经不需要的了。

基本垃圾回收算法(策略)

垃圾收集器在对堆进行回收前,第一件事情就是要确定这些对象之中哪些还“存活”着,**哪些已经“死去”。

引用计数(Reference Counting)

比较古老的回收算法。原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一
个计数。垃圾回收时,只用收集计数为 0 的对象。此算法最致命的是无法处理循环引用的
问题。

在两个对象出现循环引用的情况下,此时引用计数器永远不为 0,导致无法对它们进行回收。正是因为循环引用的存在,因此 Java 虚拟机不使用引用计数算法。(难以解决对象之间的循环引用问题)

可达性分析算法

GC Roots (垃圾回收根)起始点进行搜索,可达的对象都是存活的,不可达的对象可被回收。

标记-清除(Mark-Sweep)

:此算法执行分两阶段。

第一阶段从引用根节点开始标记所有被引用的对象,
第二阶段遍历整个堆,把未标记的对象清除。此算法需要暂停整个应用,同时,会产生内存碎片。

复制(Copying)

此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。
垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。次算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不会出现“碎片”问题。当然,此算法的缺点也是很明显的,就是需要两倍内存空间。

标记-整理(Mark-Compact)

此算法结合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,
第一阶段从根节点开始标记所有被引用对象,
第二阶段遍历整个堆,清除标记对象,不是直接对未标记对象进行处理,把存活对象“压缩”到堆的其中一块,按顺序排放。而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存,此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。

按分区对待的方式分

增量收集(Incremental Collecting):实时垃圾回收算法,即:在应用进行的同时进行垃圾回收。不知道什么原因 JDK5.0 中的收集器没有使用这种算法的。

分代收集(Generational Collecting):基于对对象生命周期分析后得出的垃圾回收算法。把对象分为年青代、年老代、持久代,对不同生命周期的对象使用不同的算法(上述方式中的一个)进行回收。现在的垃圾回收器(从 J2SE1.2 开始)都是使用此算法的。

3.3 按系统线程

串行收集使用单线程处理所有垃圾回收工作,因为无需多线程交互,实现容易,而且效率比较高。但是,其局限性也比较明显,即无法使用多处理器的优势,所以此收集适合单处理器机器。当然,此收集器也可以用在小数据量(100M 左右)情况下的多处理器机器上。

并行收集使用多线程处理垃圾回收工作,因而速度快,效率高。而且理论上CPU 数目越多,越能体现出并行收集器的优势。

并发收集:相对于串行收集和并行收集而言,前面两个在进行垃圾回收工作时,需要暂停整个运行环境,而只有垃圾回收程序在运行,因此,系统在垃圾回收时会有明显的暂停,而且暂停时间会因为堆越大而越长。

4.分代处理垃圾

试想,在不进行对象存活时间区分的情况下,每次垃圾回收都是对整个堆空间进行回收,花
费时间相对会长,同时,因为每次回收都需要遍历所有存活对象,但实际上,对于生命周期
长的对象而言,这种遍历是没有效果的,因为可能进行了很多次遍历,但是他们依旧存在。
因此,分代垃圾回收采用分治的思想,进行代的划分,把不同生命周期的对象放在不同代上,
不同代上采用最适合它的垃圾回收方式进行回收。

虚拟机中的共划分为三个代:

年轻代(Young Generation)、年老点(Old Generation)和持久代(Permanent Generation)。其中持久代主要存放的是 Java 类的类信息,与垃圾收集要收集的 Java 对象关系不大。年轻代和年老代的划分是对垃圾收集影响比较大的。

年轻代:所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉
那些生命周期短的对象。年轻代分三个区。一个 Eden 区,两个 Survivor 区(一般而言)。
大部分对象在 Eden 区中生成。当 Eden 区满时,还存活的对象将被复制到 Survivor 区(两
个中的一个),当这个 Survivor 区满时,此区的存活对象将被复制到另外一个 Survivor 区,
当这个 Survivor 去也满了的时候,从第一个 Survivor 区复制过来的并且此时还存活的对象,
将被复制“年老区(Tenured)”。需要注意,Survivor 的两个区是对称的,没先后关系,所以同
一个区中可能同时存在从 Eden 复制过来 对象,和从前一个 Survivor 复制过来的对象,而
复制到年老区的只有从第一个 Survivor 去过来的对象。而且,Survivor 区总有一个是空的。
同时,根据程序需要,Survivor 区是可以配置为多个的(多于两个),这样可以增加对象在
年轻代中的存在时间,减少被放到年老代的可能。

年老代:在年轻代中经历了 N 次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,
可以认为年老代中存放的都是一些生命周期较长的对象。

持久代:用于存放静态文件,如今 Java 类、方法等。持久代对垃圾回收没有显著影响,但是
有些应用可能动态生成或者调用一些 class,例如 Hibernate 等,在这种时候需要设置一个
比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过
-XX:MaxPermSize=进行设置。

  • Serial收集器
  • ParNew收集器
  • Parallel Scavenge收集器
  • Serial Old收集器
  • Parallel Old收集器
  • CMS收集器
  • G1收集器

上面所讲的垃圾收集算法只能算是方法论,落地实现的是垃圾收集器:

上面这些收集器大部分是可以互相组合使用的

在这里插入图片描述

新生代分为三个区域: 1个Eden和两个Survivor区

  1. 一般情况,新创建的对象会被分到Eden区,
  2. Eden区满了以后出发 MinorGC,如果依然存在,将会被移到survivor区,年龄为1,。对象在survivor去中每熬过一次Minor GC ,年龄就会增加一岁。
  3. 增加到一定程度时,会移到老年区,或者 Survivor区满了也会进入老年代。

Full GC 对整个对内存进行回收,一般是老年代,永久代内存不足的时候出发,所以应该避免创建过多的对象。

垃圾收集主要是针对堆和方法区进行。程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后就会消失,因此不需要对这三个区域进行垃圾回收。

题外 jvm面试题:

1、详细jvm内存模型
2、讲讲什么情况下回出现内存溢出,内存泄漏?
3、说说Java线程栈
4、JVM 年轻代到年老代的晋升过程的判断条件是什么呢?
5、JVM 出现 fullGC 很频繁,怎么去线上排查问题?
6、类加载为什么要使用双亲委派模式,有没有什么场景是打破了这个模式?

类与类加载器
jvm中,确定一个类的唯一性是依赖于加载这个类的类加载器和这个类本身的。只有加载类的类加载器和类本身两者都一直,jvm才会认为这个类是唯一的。

类加载器的类型

 从JVM层面来讲,有两种:
1、启动类加载器(Bootstrap ClassLoader)
该加载器由C++语言实现,属于JVM内部。
2、其他的类加载器
该类有java语言编写,属于JVM外部,全部继承于抽象类java.lang.ClassLoader

 从JAVA角度来讲,有三种:
1、启动类加载器(Bootstrap ClassLoader)
这个和上面一致,不赘述。
2、扩展类加载器(Extension ClassLoader)
负责加载<JAVA_HOME>\lib\ext 目录中的,或者被java.ext.dirs系统变量所指定的路径下的类库。
3、应用程序加载器(Application ClassLoader)
该加载器是ClassLoader中的getSystemClassLoader()方法的返回值,一般也称为系统类加载器。
负责加载用户类路径CLASSPATH所指定的类库。
7、类的实例化顺序
8、JVM垃圾回收机制,何时触发MinorGC等操作
9、JVM 中一次完整的 GC 流程(从 ygc 到 fgc)是怎样的
10、各种回收器,各自优缺点,重点CMS、G1
11、各种回收算法
12、OOM错误,stackoverflow错误,permgen space错误

为什么类加载器要采用双亲委派模式?

首先我们来看看如果不用双亲委派模式,会是什么结果。

比如我们自己创建了一个java.lang.Object类,并且放在classpath下,然后启动程序,因为java本身会有个java.lang.Object类,然后会造成java本身的Object类由启动类加载器来加载,而我们自己创建的Object类会由应用程序类加载器类加载,然后根据我们前面提到的【一个类的唯一性由加载这个类的类加载器和这个类本身来确定】,那么现在这种情况,就会造成类的混乱

而使用双亲委派模式的话,会进行限制,这种情况如果我们自己创建了一个java.lang.Object类,则会在运行时发生错误,限制了类混乱的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值