JVM 的参数类型
- 标配参数
- -version
- -help
- X 参数(了解)
- -Xint:解释执行
- -Xcomp:第一次使用就编译成本地代码
- -Xmixed:混合模式
- XX 参数
- Boolean 类型:-XX:+ 或者 - 某个属性值(+ 表示开启,- 表示关闭)
- -XX:+PrintGCDetails:打印 GC 收集细节
- KV 设置类型:-XX:key=value
- -XX:MetaspaceSize=128m
- -XX:MaxTenuringThreshold=15
- jinfo 举例,如何查看当前运行程序的配置
- Boolean 类型:-XX:+ 或者 - 某个属性值(+ 表示开启,- 表示关闭)
public class HelloGC {
public static void main(String[] args) {
System.out.println("hello GC...");
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
我们可以使用 jps -l
命令,查出进程 id
-
在使用
jinfo -flag PrintGCDetails 1933
命令查看-XX:-PrintGCDetails
可以看出默认是不打印 GC 收集细节
也可是使用jinfo -flags 1933
查看所以的参数 -
两个经典参数:-Xms 和 - Xmx(如 -Xms1024m)
- -Xms 等价于 -XX:InitialHeapSize
- -Xmx 等价于 -XX:MaxHeapSize
查看 JVM 默认值
-
查看初始默认值:-XX:+PrintFlagsInitial
-
查看修改更新:-XX:+PrintFlagsFinal
= 与 := 的区别是,一个是默认,一个是人为改变或者 jvm 加载时改变的参数
-
打印命令行参数(可以看默认垃圾回收器):-XX:+PrintCommandLineFlags
常用配置
-
-Xms
- 初始大小内存,默认为物理内存 1/64
- 等价于 -XX:InitialHeapSize
-
-Xmx
- 最大分配内存,默认为物理内存的 1/4
- 等价于 -XX:MaxHeapSize
- 生产中-Xmx与-Xms设置一样大小。因为默认空余堆内存小于40%时JVM就会增大堆内存直到设置的最大值;空余堆内存70%时,JVM就会减少堆内存直到设置的最小值。
-
-Xss
- 设置单个线程栈的大小,一般默认为 512-1024k
- 等价于 -XX:ThreadStackSize
-
-XX:MetaspaceSize
- 设置元空间大小(元空间的本质和永久代类似,都是对 JVM 规范中的方法区的实现,不过元空间于永久代之间最大区别在于,元空间并不在虚拟中,而是使用本地内存,因此默认情况下,元空间的大小仅受本地内存限制)
- 元空间默认比较小,我们可以调大一点
-
-XX:+PrintGCDetails
-
输出详细 GC 收集日志信息
-
设置 JVM 参数为: -Xms10m -Xmx10m -XX:+PrintGCDetails
-
代码
public class HelloGC { public static void main(String[] args) { byte[] bytes = new byte[20 * 1024 * 1024]; } }
-
打印结果
-
-
GC(minor日志)
-
FullGC
-
-
-Xmn
- 设置年轻代的大小
- 整个JVM内存大小=年轻代大小 + 年老代大小 + (持久代大小),持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-
-XX:SurvivorRatio
- 设置新生代中 eden 和 S0/S1 空间比例
- 默认 -XX:SurvivorRatio=8,Eden : S0 : S1 = 8 : 1 : 1
- -XX:SurvivorRatio=eden/from=eden/to
-
-XX:NewRatio
- 配置年轻代和老年代在堆结构的占比
- 默认 -XX:NewRatio=2 新生代占1,老年代占2,年轻代占整个堆的 1/3
- -XX:NewRatio=老年代/新生代
-
-XX:MaxTenuringThreshold(必选)
- 设置垃圾最大年龄,默认15
-
-XX:+AggresslveOps(必选)
-
jdk自带的魔法属性,积极的生猛的;可以将jdk优化后的新特性自动注入
-
-XX:+UseBiasedLocking(必选)
- 启用一个优化的线程锁,对于高并发访问很重要,太多的请求忙不过来它自动优化;对于格子长短不一的请求,出现阻塞,排队的现象,它也自动优化。
-
-XX:+DisableExplicitGC(必选)
- 禁止代码中显示调用GC
-
-Djava.awt.headess=true
- 避免linux环境下图形化界面无法显示的问题
-
-XX:+UseCMSCompactAtFullCollection(必选)
- 使用并发收集器时,开启对年老代的压缩
-
-XX:CMSFullGCsBeforeCompaction=50(必选)
- 上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩
-
-XX:+CMSparalleRemarkEnabled(必选)
- 降低标记停顿
-
-XX:LargePageSizeInBytes=128m(必选)
- JVM内存分页
-
-XX:+UseFastAccessorMethods
- 优化产生的 BIN,执行过程提速
-
-XX:+UseCMSInitiatingOccupancyOnly
- 只有在老年代的使用率达到阈值时才会触发CMS,一般和-XX:CMSInitiatingOccupancyFraction=68(默认,即当老年代的空间使用率达到68%时会执行一次cms回收)配合使用。
-
-XX:+DoEscapeAnalysis
- 逃逸分析栈上分配,意思是方法内局部变量(未发生逃逸)生成的实例在栈上分配,不用在堆中分配,分配完成后,继续在调用栈内执行,最后线程结束,栈空间被回收,局部变量对象也被回收。
- 一般生成的实例都是放在堆中的,然后把实例的指针或引用压入栈中
-
-XX:+EliminateAllocations (配合逃逸分析)
- 开启变量替换
- 标量:即不可被进一步分解的量,而JAVA的基本数据类型就是标量(如:int,long等基本数据类型以及reference类型等),标量的对立就是可以被进一步分解的量,而这种量称之为聚合量。而在JAVA中对象就是可以被进一步分解的聚合量。
- 替换: 通过逃逸分析确定该对象不会被外部访问,并且对象可以被进一步分解时,JVM不会创建该对象,而会将该对象成员变量分解若干个被这个方法使用的成员变量所代替。这些代替的成员变量在栈帧或寄存器上分配空间。
-
-XX:+EliminateLocks (配合逃逸分析)
- 同步消除
- 同步消除是java虚拟机提供的一种优化技术。通过逃逸分析,可以确定一个对象是否会被其他线程进行访问。如果你定义的类的方法上有同步锁,但在运行时,却只有一个线程在访问,此时逃逸分析后的机器码,会去掉同步锁运行,这就是没有出现线程逃逸的情况。那该对象的读写就不会存在资源的竞争,不存在资源的竞争,则可以消除对该对象的同步锁。
-
-XX:+UseTLAB
- TLAB全称
ThreadLocalAllocBuffer
,是线程的一块私有内存,如果设置了虚拟机参数-XX:UseTLAB
,在线程初始化时,同时也会申请一块指定大小的内存,只给当前线程使用,这样每个线程都单独拥有一个Buffer,如果需要分配内存,就在自己的Buffer上分配,这样就不存在竞争的情况,可以大大提升分配效率,当Buffer容量不够的时候,再重新从Eden区域申请一块继续使用,这个申请动作还是需要原子操作的。 - -XX:TLABSize= 表示,设置TLAB大小
- -XX:+ResizeTLAB 是否启动动态修改
- -XX:TLABRefillWasteFraction 表示,设置进入TLAB空间,单个对象大小是一个比例值,默认为64 如果,对象小于整个空间的1/64,则放在TLAB区 如果,对象大于整个空间的1/64,则放在堆区
- TLAB全称
-
-XX:+PrintVMOptions
- 打印虚拟机接收到的命令行显式参数
-
-XX:+PrintCommandLineFlags
- 打印虚拟机接收到的命令行显式和隐式参数,隐式参数可能是虚拟机启动时自动设置的
-
-XX:MaxDirectMemorySize
- 设置最大可用直接内存,默认是最大堆空间
- 直接内存适合申请次数较少、访问频繁的场合
GC与垃圾回收器
- 四种 GC 垃圾回收算法
- 引用计数
- 复制回收
- 标记清除
- 标记整理
- GC 算法是内存回收的方法论,垃圾收集其就是算法的落实的实现。
- 目前为止还没有完美的收集器的出现,更加没有万能的收集器,只是针对具体应用最适合的收集器,进行分代收集。
- 串行垃圾回收器(Serial)
- 它为单线程环境设计且只使用一个线程进行垃圾回收,会暂停所有的用户线程,所以不适合服务环境。
- 并行垃圾回收器(Parallel)
- 多个垃圾收集线程并行工作,此时用户线程是暂停的,用于科学计算、大数据处理等弱交互场景。
- 并发垃圾回收器(CMS)
- 用户线程和垃圾收集线程同时执行(不一定是并行,可能是交替执行),不需要停顿用户线程,互联网公司多用它,适用对相应时间有要求的场景。
- G1 垃圾回收器
- G1 垃圾回收器将堆内存分割成不同的区域然后并发的对其进行垃圾回收。
查看默认的垃圾回收器
- 怎么查看服务器默认垃圾收集器是哪个?
- Java -XX:+PrintCommandLineFlags
- Java 的 GC 回收的类型主要有:
- UseSerialGC,UseParallelGC,UseConcMarkSweepGC,UseParNewGC,UseParallelOldGC,UseG1GC, SerialOldGC
- Java 8 以后基本不使用 Serial Old
- 垃圾收集器(红叉部分不再推荐使用)
- 参数说明(GC日志中术语)
- DefNew : Default New Generation
- Tenured : Old
- ParNew : Parallel New Generation
- PSYoungGen : Parallel Scavenge
- ParOldGen : Parallel Old Generation
- Server/Client 模式分别是什么意思
- 最主要的差别在于:-Server模式启动时,速度较慢,但是一旦运行起来后,性能将会有很大的提升。
- 当虚拟机运行在-client模式的时候,使用的是一个代号为C1的轻量级编译器, 而-server模式启动的虚拟机采用相对重量级,代号为C2的编译器,C2比C1编译器编译的相对彻底,服务起来之后,性能更高。
- 所以通常用于做服务器的时候我们用服务端模式,如果你的电脑只是运行一下java程序,就客户端模式就可以了。当然这些都是我们做程序优化程序才需要这些东西的,普通人并不关注这些专业的东西了。其实服务器模式即使编译更彻底,然后垃圾回收优化更好,这当然吃的内存要多点相对于客户端模式。
- 新生代
- 串行 GC (Serial/ Serital Copying)
- -XX:+UseSerialGC 可以指定新生代和老年代都是用串行收集器;client模式下它是默认的收集器;独占式单线程垃圾回收
- 并行 GC (ParNew)
- 当使用-XX:+UseparNewGC时,工作线程可以使用 -XX:ParallelGCThreads来指定,CPU数量小于8该值与CPU数量相等;反之大于可设置为 3+((5*cpu)/8)
- 并行回收 GC (Parallel/ Parallel Scanvenge)非常关注系统的吞吐量
- -XX:MaxGCPauseMillis:设置垃圾收集最大停顿时间,是一个大于0的数,当ParallelGC工作时,会尽可能吧停顿时间控制在这个范围内;若数值过小会导致GC频繁,从而增加了垃圾回收总时间,降低了吞吐量。
- -XX:GCTimeRatio:设置吞吐量,是一个0–100之间的整数;系统将不会花费超过 **1+(1+n)**的时间用于垃圾回收;默认值99。
- 串行 GC (Serial/ Serital Copying)
- 老年代
- 串行 GC (Serial Old/ Serial MSC)
- 并行 GC (Parallel Old/ Parallel MSC)
- 并发标记清除 GC (CMS)
- 是一种以获取最短回收停顿时间为目标的收集器,适合应用在互联网站或者 B/S 系统的服务器上,这个类应用尤其重视服务器的响应速度,希望系统停顿时间最短。
- CMS 非常适合堆内存大、CPU 核数多的服务器端应用,也是 G1 出现之前大型应用首选收集器。
- 并发停顿比较少,并发指的是与用户线程一起执行。
- 过程
- 初始标记(initail mark):只是标记一下 GC Roots 能直接关联的对象,速度很快,需要暂停所有的工作线程
- 并发标记(concurrent mark 和用户线程一起):进行 GC Roots 的跟踪过程,和用户线程一起工作,不需要暂停工作线程。
- 预清理:清理前准备以及控制停顿时间。可以通过 -XX:+CMSPrecleaningEnabled控制开启与关闭。预清理是并发的,除了为正式清理做准备和检查外,这个阶段还会尝试控制一次停顿时间。由于重新标记是独占cpu的,若新生代gc后,立即触发重新标记,那么一次停顿时的时间很长。为避免这种情况,预清理时会刻意等待一次新生代gc的发生,然后根据历史性能数据预测下一次新生代gc的时间,然后在当前时刻与预测时间的中间时刻景进行重新标记。这样尽量避免新生代gc与重新标记重合,从而减少停顿时间。
- 重新标记(remark):为了修正在并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,仍然需要暂停所有的工作线程。
- 并发清除(concurrent sweep 和用户线程一起):清除 GC 不可达对象,和用户线程一起工作,不需要暂停工作线程,基于标记结果,直接清除。由于耗时最长的并发标记和并发清除过程中,垃圾收集线程和用户线程可以一起并发工作,所以总体来看 CMS 收集器的内存回收和用户线程是一起并发地执行。
- 并发重置
- 优缺点
- 优点:并发收集停顿低
- 缺点:并发执行对 CPU 资源压力大,采用的标记清除算法会导致大量碎片
- 由于并发进行, CMS 在收集与应用线程会同时增加对堆内存的占用,也就是说,CMS 必须要在老年代堆用尽之前完成垃圾回收,否者 CMS 回收失败,将触发担保机制,串行老年代收集器将会以 STW 的方式进行一次 GC,从而造成较大的停顿时间。
- 标记清除算法无法整理空间碎片,老年代空间会随着应用时长被逐渐耗尽,最后将不得不通过担保机制对堆内存进行压缩。CMS 也提供了参数 -XX:CMSFullGCsBeForeCompaction (默认0,即每次都进行内存整理) 来指定多少次 CMS 收集之后,进行一次压缩整理空间碎片。
- CMS参数设置
- 并发线程数量:-XX:ConcGCThreads或-XX:ParallelGCThreadsCMS默认的并发线程数量((ParallelGCThreads+3)/4)
- 堆内存使用率的阀值:-XX:CMSInitiatingOccupancyFraction默认68.即当老年代的空间使用率达到68%时会执行一次cms回收。
- 回收Perm区Class数据:-XX:+CMSClassUnloadingEnabled
- 垃圾收集器配置代码总结
- 如何选择垃圾收集器
- 单 CPU 或者小内存,单机程序:-XX:UseSerialGC
- 多 CPU 需要最大吞吐量,如后台计算型应用:-XX:UseParallelGC 或者 -XX:UseParallelOldGC
- 多 CPU 追求低停顿时间,需要快速响应,如互联网应用:-XX:+UseConcMarkSweepGC
G1垃圾收集器
-
以前收集器的特点
- 年轻代和老年代是各自独立且连续的内存块
- 年轻代收集器使用 eden + S0 + S1 进行复制算法
- 老年代收集必须扫描整个老年代区域
- 都是以尽可能的少而快速地执行 GC 为设计原则
-
G1 是什么
- G1 是一种面向服务端的垃圾收集器,应用在多核处理器和大容量内存环境中,在实现高吞吐量的同时,尽可能的满足垃圾收集器的暂停时间要求。
- 像 CMS 收集器一样,能与应用程序线程并发执行,整理空闲空间更快,需要更多的时间来预测 GC 停顿时间,不希望牺牲大量的吞吐性能,不需要更大的 JAVA Heap。
- G1 收集器的设计目的是取代 CMS 收集器,同时与 CMS 相比,G1 垃圾收集器是一个有整理内存过程的垃圾收集器,不会产生很多内存碎片。G1 的 Stop The World 更可控,G1 在停顿上添加了预测机制,用户可以指定期望的停顿时间。
- G1 是在 2012 年才在 jdk.1.7u4 中可以使用,在 jdk9 中将 G1 变成默认垃圾收集器来代替 CMS。它是以款面向服务应用的收集器。
- 主要改变是 Eden、Survivor 和 Tenured 等内存区域不再是连续的,而是变成了一个个大小一样的 region,每个 region 从 1M 到 32M 不等,一个 region 有可能属于 Eden、Survivor 或者 Tenured 内存区域。
-
特点
- 并行性:G1在回收期间,可以有多个gc线程同时工作,有效利用多核计算能力。
- 并发性:G1可以与应用程序交替执行,部分工作可以同时执行,不会再整个回收期间完全阻塞应用程序。
- 分代GC:G1是一个分代收集器,同时兼顾年轻代和老年代。
- 空间整理:G1在收集期间,会是党的对象移动,不像CMS只是简单地标记清理对象,在若干次gc之后才进行一次碎片整理;而G1不同,在每次gc之后都会有效的复制对象,减少空间碎片。
- 可预见性:由于分区的原因,G1可以选取部分区域进行内存回收,这样缩小了范围,对全局停顿也有较好的控制。
-
底层原理
-
Region 区域化垃圾收集器:最大好处是化整为零,避免全内存扫描,只需要按照区域来进行扫描即可。
-
Region
G1的内存结构和传统的内存空间划分有比较的不同。G1将内存划分成了多个大小相等的Region(默认是512K),Region逻辑上连续,物理内存地址不连续。同时每个Region被标记成E、S、O、H,分别表示Eden、Survivor、Old、Humongous。其中E、S属于年轻代,O与H属于老年代。
H表示Humongous。从字面上就可以理解表示大的对象(下面简称H对象)。当分配的对象大于等于Region大小的一半的时候就会被认为是巨型对象。H对象默认分配在老年代,可以防止GC的时候大对象的内存拷贝。通过如果发现堆内存容不下H对象的时候,会触发一次GC操作。 -
回收步骤
- 参看:G1从入门到放弃
-
四步过程
-
初始标记:标记从根节点直接可达的对象,会伴随一次新生代GC,会全局停顿,应用程序线程也会停止。
-
跟区域扫描:初始化标记之后,eden被清空,并且存活的对象会被移动到survivor区。在这个阶段,将扫描由survivor区直接可达的老年区域,并标记这些直接可达的对象。由于跟区域扫描和新生代GC不能同时执行,若此时要进行新生代GC,就需要到等待跟区域扫描之后进行,因此会比较耗时。
-
并发标记:和CMS一样并发标记将扫描并检查整个堆的存活对象,这是个并发的过程,并且这个过程可以被一次新生代GC打断。
-
重新标记:和CMS一样,重新标记也会使应用程序停顿。由于此过程中,应用程序依然运行,因此标记结果可能需要修正,所以次过程是对上一次标记的补充。
-
独占清理:这个过程会引起停顿。他讲计算各个区域的存活对象和GC回收比例进行排序,识别可供混合回收的区域。
-
并发清理:此过程会识别并清理完全空闲的区域,不会引起停顿。
-
Linux指标命令
- 整机:top
-
CPU:vmstat
-
内存:free
-
硬盘:df
-
磁盘IO:iostat
-
网络IO:ifstat
-
cpu信息
-
总核数 = 物理CPU个数 X 每颗物理CPU的核数
-
总逻辑CPU数 = 物理CPU个数 X 每颗物理CPU的核数 X 超线程数
-
查看物理CPU个数
-
cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l
-
-
查看每个物理CPU中core的个数(即核数)
-
cat /proc/cpuinfo| grep "cpu cores"| uniq
查看逻辑CPU的个数
-
cat /proc/cpuinfo| grep "processor"| wc -l
-
-
常用GC参数