运行时数据区
- 程序计数器
- 虚拟机栈
- 本地方法栈
- 堆
- 方法区
方法区存储内容
类的所有字段和方法字节码,以及一些特殊方法如构造方法,接口代码也在此定义。也就是静态变量 + 常量 + 类信息(构造方法 / 接口定义)+ 运行时常量池都存在该方法区中。
永久代和元空间
- 永久代:永久代在jdk1.7之后就被元空间给取代了,永久代逻辑结构上属于堆,但是物理上不属于堆,会出现OOM异常。
- 元空间:元数据区取代了永久代,本质和永久代类似逻辑结构上属于堆,区别在于元数据区并不在虚拟机中,而是使用本地物理内存,永久代在虚拟机中,元数据区也有可能发生OutOfMemory异常。
这样就可以不用在单独为方法区去做一个内存管理了。
GC Root的对象
- 虚拟机栈中引用的对象
- 方法区中静态属性引用的对象
- 方法区常量引用的对象
- 本地方法栈中JNI引用的对象
- Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象,还有系统类加载器
- 所有被同步锁持有的对象
- 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等
常见的OOM
- 堆内存溢出,堆上对象分配空间不足,有OutOfMemoryError。
解决:通过内存映像分析工具(Eclipse Memory Analyzer)对 Dump 出来的堆转储快照进行分析,确定是内存泄露还是内存溢出。如果是内存泄露,可以查看引用链,定位到产生内存泄漏代码的具体位置;如果是内存溢出,检查堆参数与机器内存差距,看看是否有向上调整的空间,另外可以检查是否存在对象生命周期过长,持有状态时间过长、存储结构设计不合理的地方。
- 虚拟机栈和本地方法栈溢出
- 如果线程请求的栈深度大于虚拟机栈所允许的最大深度,将抛出StackOverflowError。
- 如果虚拟机栈的内存允许动态扩展,当扩展栈容量无法申请到足够的内存时,抛出OutOfMemoryError异常,否则抛出StackOverflowError。
- 方法区和运行时常量池溢出
- 本地直接内存溢出
引用
在 java 中主要有以下四种引用类型:强引用,软引用,弱引用,虚引用。不同的引用类型主要体现在 GC 上:
- 强引用
如果一个对象具有强引用,它就不会被垃圾回收器回收。即使当前内存空间不足,JVM 也不会回收它。而是抛出 OutOfMemoryError 错误。使程序异常终止。如果想中断强引用和某个对象之间的关联。可以显式地将引用赋值为 null, 这样一来的话.JVM 在合适的时间就会回收该对象。
- 软引用
在使用软引用时,如果内存的空间足够,软引用就能继续被使用而不会被垃圾回收器回收,只有在内存不足时,软引用才会被垃圾回收器回收。
- 弱引用
具有弱引用的对象拥有的生命周期更短暂,因为当 JVM 进行垃圾回收时,如果这个对象只被弱引用关联(没有任何强引用关联他),那么这个对象就会被回收,不过由于垃圾回收器是一个优先级较低的线程,所以并不一定能迅速发现弱引用对象。弱引用的作用是回收不再使用的键值对,不用弱引用只要桶活着,就不会被回收。
- 虚引用
如果一个对象仅持有虚引用,那么它相当于没有引用,在任何时候都可能被垃圾回收器回收。唯一的作用是能在这个对象被收集器回收时收到一个系统通知,以此判断垃圾回收器的频率。
之所以出现不同的引用是 Java 对垃圾回收不可控的妥协。
堆内存分配策略
- 对象优先分配在Eden区,如果Eden区没有足够的空间进行分配时,虚拟机执行一次MinorGC。
- 大对象直接进入老年代(需要大量连续内存空间的对象)。这样做的目的是避免在Eden区和两个Survivor区之间发生大量的内存拷贝(新生代采用复制算法收集内存)。
- 长期存活的对象进入老年代。虚拟机为每个对象定义了一个年龄(Age Count)计数器,如果对象经过了1次Minor GC那么对象会进入Survivor区,之后每经过一次Minor GC那么对象的年龄加1,直到达到阀值(默认15次),对象进入老年区。
- 动态判断对象的年龄。如果Survivor区中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代。
- 空间分配担保。在Minor GC前,虚拟机会检查老年代最大可用连续空间是否大于新生代所有对象总空间,如果成立就执行Minor GC;否则检查HandlePromotionFailure设置值是否允许担保失败,如果不允许,直接执行Full GC;否则检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于的话尝试Minor GC;否则改为Full GC。
FULL GC触发条件
- 新生代空间不足
- 永久代空间满了
- CMS GC时出现promotion failed和concurrent mode failure
- 老年代最大可用的连续空间小于历次晋升到老年代对象的平均大小
判断对象是否存活
会有两次检查。第一次扫描并标记不可达对象,并在其中进行筛选没有事先 finalize () 或已经调用过 finalize () 的对象,让其死亡。第二次检查,会判断该对象是否有必要执行 finalize () 方法,稍后会由优先级较低的 Finalizer 线程执行,如果在执行前被重新引用,可以避免死亡。
三色标记算法
它是描述追踪式回收器的一种有效的方法,利用它可以推演回收器的正确性。
我们将对象分成三种类型:
- 黑色:根对象,或者该对象与它的子对象都被扫描过(对象被标记了,且它的所有field也被标记完了)。
- 灰色:对象本身被扫描,但还没扫描完该对象中的子对象(它的field还没有被标记或标记完)。
- 白色:未被扫描对象,扫描完成所有对象之后,最终为白色的为不可达对象,既垃圾对象(对象没有被标记到)。
对象消失问题
- 赋值器插入了一条或多条从黑色对象到白色对象的新引用
- 赋值器删除了全部从灰色对象到该白色对象的直接或间接引用
解决:增量更新、原始快照。
增量更新
当黑色对象插入新的指向白色对象的引用关系时,就记录新插入的引用记录,等并发扫描结束后,以这些黑色对象为根,重新扫描,即黑色对象一旦新插入了指向白色对象的引用之后,就变回了灰色对象。
原始快照
当灰色对象要删除指向白色对象的引用关系时,就讲这个要删除的引用记录下来,在并发扫描结束后,将记录过的引用关系中的灰色对象为根,重新扫描一次,即无论引用关系删除与否,都会按照刚刚开始扫描的那一刻的对象图快照来进行搜索。
垃圾回收算法
- 标记清除算法
缺点:标记和清除的两个动作效率都不高;清楚后会产生大量不连续的空间碎片。
- 复制算法
将可用内存按容量划分为大小相等的两块,每次只使用其中的一块,一块用完了,就将存活着的对象复制到另外一块上面。
优点:没有碎片问题。
缺点:成本太高,内存小了一半。
- 标记整理算法
根据老年代的特点,提出了此算法。标记过程仍然与 “标记 - 清除” 算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,最后直接清理掉端边界以外的内存。
- 分代收集算法
新生代使用复制算法;老年代使用标记清除 / 标记整理算法。
垃圾回收器
Serial收集器
新生代收集器,它是最基本、发展历史最悠久的收集器。是一个单线程收集器,在垃圾收集时,必须暂停其他所有的工作线程,直到收集结束。适合运行在 Client 模式下的虚拟机。
Serial Old收集器
Serial 收集器的老年代版本,同样也是单线程收集器,使用 “标记 - 整理” 算法。
ParNew收集器
新生代收集器,是 Serial 收集器的多线程版本,除了 Serial 收集器外,只有它能与 CMS 收集器配合工作。
Parallel Scavenge收集器
新生代收集器,也是使用复制算法的收集器,又是并行的多线程收集器。它更关注于达到一个可控制的吞吐量。其中,吞吐量 = 运行用户代码时间 /(运行用户代码时间 + 垃圾收集时间)。
除此之外,它还有自适应调节的特性,虚拟机根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量,这被称为 GC 自适应的调节策略。它是与 ParNew 收集器的一个重要区别。
Parallel Old收集器
是 Parallel Scavenge 收集器的老年代版本,使用多线程和 “标记 - 整理” 算法。
CMS收集器
老年代收集器,CMS 收集器是一种以获取最短回收停顿时间为目标的收集器,使用 “标记 - 清除” 算法。回收过程如下:
- 初始标记:需要Stop The World,标记能直接关联到的对象。
- 并发标记:可以与用户一起工作,进行GC Roots Tracing的过程。
- 重新标记:需要Stop The World,为了修正并发标记期间因用户程序继续运作而导致标记产生的那一部分对象的标记记录,停顿时间比初始标记稍长一些,远短于并发标记时间。
- 并发清除:可以与用户一起工作。
优点:并发收集,低停顿。
缺点:吞吐量问题,CMS 对 CPU 资源敏感,会因为占用一部分线程,导致应用程序变慢,使吞吐量降低;浮动垃圾问题,并发清除阶段产生的垃圾叫做浮动垃圾;碎片问题。
G1收集器
在 1.7 时被认为达到足够的商用程度,是收集器技术发展的最前沿成果之一。它是一款面向服务端应用的垃圾收集器。
特点:
- 并行与并发
可以使用多个 CPU 来缩短 Stop-The-World 时间。
- 分代收集
可以管理整个 GC 堆(G1 将内存划分为多个大小相等的区域,并维护一个优先列表,优先回收回收价值最大的 Region),对不同年龄对象采用不同策略。
- 空间整合
整体上采用 “标记整理”,局部采用 “复制” 算法实现,所以不会产生碎片。
- 可预测的停顿
收集过程:
- 初始标记:需要停顿,标记GC Roots能直接关联到的对象。
- 并发标记:做可达性分析,找出存活的对象。
- 最终标记:需要停顿,但可以并行执行,为了修正在并发标记期间内用户程序继续运作而导致标记产生变动的那一部分标记记录。
- 筛选回收:需要停顿,对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划。
- 分成许多个Region后,如何解决跨Region引用造成的问题?
双向卡表来维护,使得内存空间消耗额外 10% 至 20% 的堆内存。
- 并发标记阶段如何保证收集线程与用户线程互不干扰地运行?
CMS 采用增量更新算法实现,G1 采用原始快照算法实现。G1 为每个 Region 设计了两个名为 TAMS 的指针,把 Region 中的一部分空间划分出来用于并发回收过程中的新对象分配,并发回收时新分配的对象地址必须要在这两个指针位置以上。
- 怎样建立可预测的停顿模型?
G1 收集器的停顿预测模型是以衰减均值为理论基础来实现的,在垃圾收集过程中,G 收集器会记录每个 Region 的回收耗时、每个 Region 记忆集里的脏卡数量等各个可测量的步骤花费的成本,并分析得出平均值、标准偏差、置信度等统计信息。
相较于CMS
优点:
- 可以指定最大停顿时间
- 分Region的内存布局
- 按收益动态确定回收集
- 整体标记-整理,局部标记-复制的特性使之没有内存空间碎片
缺点:
- 内存占用高,每个Region都要维护一份卡表
- 执行负载高,G1需要用异步队列处理写前屏障与写后屏障,写屏障是指在发生引用关系变更时,对卡表进行更新
Shenandoah收集器
OracleJDK 不存在但 OpenJDK 中存在的一款收集器,与 G1 有着高度的相似性。主要区别如下:
- 支持并发的整理算法
- 默认不使用分代收集,即不存在新生代Region或者老年代Region的存在
- 放弃维护记忆集,而去维护一个连接矩阵来记录跨Region的引用关系
工作过程:
- 初始标记
标记与 GC Roots 直接关联的对象。
- 并发标记
遍历对象图,标记出全部可达的对象,与用户线程并发。
- 最终标记
处理剩余的 SATB 扫描,并统计出回收价值最高的 Region,将这些 Region 构成一组回收集。
- 并发清理
清理整个区域都没有存活对象的 Region,与用户线程并发。
- 并发回收
把回收集中的存活对象复制一份到其他未被使用的 Region,与用户线程并发。
- 初始引用更新
把堆中所有指向旧对象的引用修正到复制后的新地址。并未执行具体处理,只是确保所有回首阶段中进行的收集器线程都已完成分配给它们的对象移动任务。
- 并发引用更新
真正开始进行引用更新操作,与用户线程并发。与并发标记不同,不需要沿着对象图搜索,只需要按照内存物理地址的顺序,线性地搜索出引用类型,把旧值改为新值。
- 最终引用更新
解决堆中引用更新后,还要修正存在于 GC Roots 中的引用。
- 并发清理
经过并发回收和引用更新后,回收集中的 Region 已经没有存活对象了,再调用一次并发清理过程来回收这些 Region 的内存空间,供以后的新对象分配使用,与用户线程并发。
转发指针
并发回收时,可能用户线程会对被移动对象不断地做读写访问,所以会有一个转发指针的概念,即在对象头前设置一个新的引用字段,在不处于移动情况下,引用指向对象自己。当有了新副本时,只需要修改旧对象上面的引用地址,这样就可以统一转发到对新地址的访问。
然而,转发指针也会面临写问题所造成的线程安全问题,即修改旧值的操作未适用到新副本上,实际上,Shenandoah 收集器是通过 CAS 操作来保证并发时对象的访问正确性的。
ZGC收集器
ZGC 收集器是一款给予 Region 内存布局的,不设分代的,使用了读屏障、染色指针和内存多重映射等技术实现可并发的标记 - 整理算法的,以低延迟为首要目标的一款垃圾收集器。除了与 GC Roots 相关的几乎全程可并发。可将任何堆停顿都降低到 10ms 内。
Regionshifangle
ZGC 的 Region 分为了三类:
小型 Region:容量固定为 2MB,用于放置小于 256KB 的小对象。
中型 Region:容量固定为 32MB,用于放置大于等于 256KB 但小于 4MB 的对象。
大型 Region:容量不固定,可以动态变化,但必须是 2MB 的整数倍,用于放置 4MB 或以上的大对象。每个大型 Region 中只会放置一个大对象。
染色指针
染色指针是一种直接将少量额外的信息存储在指针上的技术。
优势:
- 染色指针可以使得一旦某个Region的存活对象被移走之后,这个Region立即就能够被释放和重用掉,而不必等待整个堆中所有指向该Region的引用都被修正后才能清理。
- 大幅减少在垃圾收集过程中内存屏障的使用数量。
- 染色指针可以作为一种可扩展的存储结构用来记录更多与对象标记、重定位过程相关的数据,以便日后进一步提高性能。
虚拟内存映射技术
染色指针有个前置条件,即操作系统需要支持重定义指针的几位。在 Linux/x86-64 平台上的 ZGC 使用了多重映射将多个不同的虚拟内存地址映射到同一个物理内存地址上,这是一种多对一映射。
把染色指针的标志位看作是地址的分段符,那只要将这些不同的地址段都映射到同一个物理内存空间,经过多重映射转换后,就可以使用染色指针正常寻址了。
工作过程
- 并发标记
做可达性分析,前后也要经过类似 G1 的初始标记、最终标记的短暂停顿,唯一不同的是标记阶段不是在对象上进行的,而是更新染色指针的 Marked 0、Marked 1 标志位。
- 并发预备重分配
根据特定的查询条件统计得出本次收集过程要清理哪些 Region,将这些 Region 组成重分配集。与 G1 不同的是,它扫描的是全堆,而非以收益优先的增量回收,以此减少记忆集的维护成本。
- 并发重分配
把重分配集中的存货对象复制到新的 Region 上,并为重分配集中的每个 Region 维护一个转发表,记录从旧对象到新对象的转向关系。当用户线程并发访问了位于重分配集中的对象,这次访问会被预置的内存屏障捕获,然后立即根据 Region 上的转发表记录讲访问转发到新复制对象上,同时修正更新该引用的值,使其直接指向新对象,这被称为指针自愈。好处是只有第一次访问旧对象会陷入转发,也就只是慢一次。
- 并发重映射
修正整个堆中指向重分配集中旧对象的所有引用。由于指针自愈的特性,所以这一阶段并不急,可以合并到下次的并发标记阶段去完成。一旦完成,原来的转发表就可以被释放了。
劣势
不能承受对象过快的分配速率,因为它没有分代。
Epsilon收集器
不回收垃圾,而是只负责堆的管理与布局、对象的分配、与解释器的协作、与编译器的协作、与监控子系统协作等,适合只运行数分钟甚至数秒的服务。
创建对象的步骤
- 检查
检查指令的参数是否能在常量池中定位到一个类的符号引用;检查这个符号引用代表的类是否已经被加载、解析和初始化过,如果没有就执行类加载。
- 分配内存
在类加载后,对象所需的内存已经可以确定了。
-
指针碰撞
指的是在堆中,空闲区和非空闲区界限分明,中间放着一个指针作为分界点的指示器,所分配的过程就只是将指针向空闲空间的那边挪动一段与对象大小相等的距离。适用于 Serial、ParNew 等带 Compact 过程的收集器。
-
空闲列表
指的是已使用内存和空闲区内存交错的情况下,没办法指针碰撞,而需要维护一个列表,记录内存的可用和不可用,分配时进行更新,适用于基于 Mark-Sweep 算法的 CMS 收集器。
对分配内存空间动作进行同步处理 —— 虚拟机采用了 CAS 配上失败重试的方式保证更新的原子性。
- 初始化
将分配到的内存空间都初始化为零值(不包括对象头),可提前至 TLAB(本地分配缓存区)分配前初始化。初始化保证了不赋初值就可直接使用,程序能访问到这些字段的数据类型所对应的零值。
- 设置
对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到元数据信息、对象的哈希码、对象的 GC 分代年龄等信息。
- 执行init
以上步骤来说,虚拟机已经产生了一个对象,但从 Java 程序视角来看,对象创建才刚开始,因为还没有执行 (),这一步将对象按照程序员的意愿进行初始化,例如属性的填充。
对象创建过程中分配内存造成的线程安全问题
- CAS+重试机制
- 本地线程分配缓冲(TLAB)
把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在 Java 堆中预先分配一小块内存,称为 TLAB,只有本地缓冲区用完了,分配新的缓存区时才需要同步锁定。
对象的访问定位
- 句柄
堆中划分出一块区域作为句柄池,reference 中存储的是句柄地址,句柄中包含了对象实例数据与类型数据各自具体的地址信息。
优点:句柄地址是稳定的,要修改只需句柄内的实例数据指针,而不需要修改 reference。
- 直接指针
reference 存储的是对象地址,对象地址包含方法区中的对象类型数据。
优点:节省一次指针定位的时间开销。
类加载过程
- 加载
通过一个类的全限定名获取定义此类的二进制字节流,将这个字节流所代表的的静态存储结构转化为元空间的运行时数据结构,同时在内存中生成一个代表这个类的 java.lang.Class 对象,作为元空间这个类的各种数据的访问入口。
- 验证
确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并不危害虚拟机自身的安全。
-
文件格式验证
-
元数据验证
-
字节码验证
-
符号引用验证
-
准备
为类变量分配内存并设置初始值,内存都将在元空间中进行分配(被 static 修饰的变量,不包括实例变量),且分配的初始值是各个对象的类初始值。(final 修饰的 static 变量除外,其在此分配对应的值)
- 解析
将常量池内的符号引用替换为直接引用的过程。
- 类或接口的解析
- 字段解析
- 类方法解析
- 接口方法解析
符号引用:
符号引用以一组符号来描述所引用的目标,可以是任何形式的字面量。
直接引用:
直接引用可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。
- 初始化
类加载过程的最后一步,真正开始执行类中定义的 Java 字节码。(执行 () 方法的过程,为静态变量赋值)
双亲委派机制
工作过程
- 收到类请求;
- 请求委派给父类;
- 到启动类时,如果找不到所需的类,就传递给子加载器。
使用双亲委派的好处
- 保证稳定性,类随着类加载器具有统一优先级的层次关系;
- 虚拟机只有在两个类的类名相同且加载该类的加载器均相同的情况下才判定这是一个类。若不采用双亲委派机制,同一个类有可能被多个类加载器加载,这样该类会被识别为两个不同的类,相互赋值时会有问题。
破坏双亲委派机制的场景
- 基础类要调用用户的代码
一个典型的例子就是 JNDI 服务,JNDI 现在已经是 Java 的标准服务,它的代码由启动类加载器去加载,但 JNDI 的目的就是对资源进行集中管理和查找,它需要调用由独立厂商实现并部署在应用程序的 ClassPath 下的 JNDI 接口提供者的代码,但启动类加载器不可能 “认识” 这些代码。
为了解决这个问题,Java 团队引入了线程上下文类加载器 (Thread Context ClassLoader)。有了线程上下文加载器,JNDI 服务就可以使用它去加载所需要的 SPI 代码,也就是父类加载器请求子类加载器去完成类加载的动作,这种行为实际上就是打通了双亲委派模型层次结构来逆向使用类加载器,实际上已经违背了双亲委派模型的一般性原则,但这也是无可奈何的事情。Java 中所有涉及 SPI 的加载动作基本上都采用这种方式,例如 JNDI、JDBC、JCE、JAXB 和 JBI 等。
- 用户对程序动态性的追求
所谓的动态性是指代码热替换、模块热部署等,简答的说就是机器不用重启,只要部署上就能用。
OSGi 实现模块化热部署的关键则是它自定义的类加载器机制的实现。每一个程序模块 (Bundle) 都有一个自己的类加载器,当需要更换一个 Bundle 时,就把 Bundle 连同类加载器一起换掉以实现代码的热替换。在 OSGi 环境下,类加载器不再是双亲委派模型中的树状结构,而是进一步发展为更加复杂的网状结构。
打破的方法
重写 ClassLoader 类中的 loadClass() 方法即可打破,重写 findClass () 是不会打破的,它的作用是加载无法被父类加载器加载的类。
Tomcat的类加载器
- CommonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;
- CatalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;
- SharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见;
- WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见。
- CommonClassLoader能加载的类都可以被Catalina ClassLoader和SharedClassLoader使用,从而实现了公有类库的共用,而CatalinaClassLoader和Shared ClassLoader自己能加载的类则与对方相互隔离。
- WebAppClassLoader可以使用SharedClassLoader加载到的类,但各个WebAppClassLoader实例之间相互隔离。
- JasperLoader的加载范围仅仅是这个JSP文件所编译出来的那一个.Class文件,它出现的目的就是为了被丢弃:当Web容器检测到JSP文件被修改时,会替换掉目前的JasperLoader的实例,并通过再建立一个新的Jsp类加载器来实现JSP文件的HotSwap功能。
双亲委派模型要求除了顶层的启动类加载器之外,其余的类加载器都应当由自己的父类加载器加载。很显然 Tomcat 已经打破了双亲委派模型,为了实现隔离性,每个 webappClassLoader 加载自己的目录下的 class 文件,不会传递给父类加载器。如果 tomcat 的 Common ClassLoade 想加载 WebApp ClassLoader 中的类,可以使用线程上下文加载器实现。
JVM性能调优
常用命令
- jps
显示系统内所有的 HotSpot 虚拟机进程。
- jstat
虚拟机统计信息监视工具。
- jinfo
Java 配置信息工具。
- jmap
Java 内存映像工具。
- jhat
虚拟机堆转储快照分析工具。
- jstack
Java 堆栈跟踪工具。
- HSDIS
JIT 生成代码反汇编。
常用工具
- JConsole
Java 监视与管理控制台。
- VsualVM
多合一故障处理工具。
常用参数
参数说明
- 标准参数(-),所有的JVM实现都必须实现这些参数的功能,而且向后兼容;
- 非标准参数(-X),默认jvm实现这些参数的功能,但是并不保证所有jvm实现都满足,且不保证向后兼容;
- 非Stable参数(-XX),此类参数各个jvm实现会有所不同,将来可能会随时取消,需要慎重使用。
在选项名前用 “+” 或 “-” 表示开启或关闭特定的选项,例:
-XX:+UseCompressedOops:表示开启 压缩指针
-XX:-UseCompressedOops:表示关闭 压缩指针
常见参数设置
- -Xms:初始堆大小
- -Xmx:最大堆大小
- -Xmn:新生代大小
- -Xss:每个线程的虚拟机栈大小
- -XX:NewSize:新生代初始大小
- -XX:NewSize:新生代最大值
- -XX:MetaspaceSize:设置元数据空间初始大小(取代-XX:PermSize)
- -XX:MaxMetaspaceSize:设置元数据空间最大值(取代之前-XX:MaxPermSize)
- -XX:+PrintGCDetails:打印GC信息