虚拟机的组成部分
JVM都由哪些部分组成,运行流程是什么?
- 类加载器:根据完全限定名将class文件加载到 运行时数据区中的方法区中,作为我们访问这个类的入口。
- 运行时数据区:由JVM管理的内存空间。
- 执行引擎:翻译字节码文件并执行class文件中的指令
- 本地接口:与本地方法库交互,是和其它编译语言交互的接口。
运行流程:首先通过类加载器(ClassLoader)将字节码文件加载到虚拟机运行时数据区中的方法区内,由于方法区中存放的是JVM的指令集规范,并不能被底层操作系统直接执行,需要命令解析器执行引擎( Execution Engine)将字节码文件翻译成汇编语言(可以被计算机直接识别),再交给CPU去执行,这个过程需要调用其它语言的本地库接口(native)来实现整个程序的功能。
讲一下运行时数据区?
java程序在运行的时候会将内存划分为不同的区域,每块区域都有自己的用途,以及创建和销毁时间。有些根据JVM的创建而存在,有些根据线程的创建而存在。
JVM的运行时数据区里都有那些?各自功能是什么?
程序计数器:当前线程所执行的字节码指令的行号指示器,字节码解析器的工作就是通过改变程序计数器的值,来选取下一条需要执行的指令(分支、循环、跳转、异常处理、线程恢复等功能)
为什么会有程序计数器?因为线程是没有记忆能力的。
java虚拟机栈:每次创建一个线程的同时就会创建一个虚拟机栈,虚拟机栈中包含栈帧,每次调用一个方法就会在虚拟机栈中创建一个栈帧
栈帧里面放的到底是什么?局部变量表、操作数栈、动态链接、方法出口等。
本地方法栈:用来管理本地方法,用C实现的 Native Method 方法
堆:用于存放对象实例,被所有线程共享
方法区:用来存放已被加载的类信息、常量、静态变量、编译后的代码class文件,被所有线程共享
程序计数器(重点理解)
- 程序计数器是一块较小的内存空间
- 由于JVM的多线程是通过线程之间的轮流切换并分配处理器执行时间的方式来实现的,一个处理器只能处理一个线程的指令,为了能让线程在切换后恢复正常,每条线程都有一个独立的程序计数器,单个线程私有,各个线程之间互不影响,称之为“线程私有”的内存空间
总结:也可以理解为是线程计数器(线程记事本)
例子(转载自某大佬,清晰易懂):
- 线程A正在看直播
- 线程B突然打了一个视频电话,中断了线程A正在观看的直播
- 然后,视频通话结束,线程A应该干什么?
总结:线程是最小的执行单位,他不具备记忆功能,他只负责去干,那这个记忆就由:程序计数器来记录
讲一下虚拟机栈(重点理解)
- 虚拟机栈是线程私有,他的生命周期跟线程相同
- 虚拟机栈用来描述java方法的内存模型:每调用一个方法,就会创建一个栈帧用来表示。虚拟机栈遵循栈的数据结构:遵循先进后出,新来的在上面也就是压栈。
解释:虚拟机栈中是有单位的,这个单位就是栈帧,每一个方法就是一个栈帧,栈帧用来存储:局部变量表、操作数栈、动态链接、方法出口等信息
解释栈帧
- 局部变量表:局部变量表又被称为局部变量数组或本地变量表,是用来存储方法中的参数、定义的局部变量、returnAddress(方法return后需要执行的字节码指令的地址)
- 操作数栈:操作数栈就是运行操作的地方,他是jvm虚拟机中一个用来计算的临时数据储存区,她会读取我们的代码(int i = 1+1;)并执行,最终将计算后的结果放入局部变量表中,
- 动态链接:我们的方法中调用了其他的方法,例如userDao.findByUId(),要链接到别的**地方(方法)去,储存链接(地址)**的地方
- 出口:正常情况下就是return回到调用的地方,非正常情况会抛出异常
一个方法调用了另一个方法会创建栈帧吗?递归调用呢?
都会创建:如果一个栈中有动态链接调用别的方法,就会创建新的栈帧。栈中的顺序A方法调用了B方法,B方法会放到A方法上面。 这个地方还有疑问不确定!!!
** 2024-10-29 15:33前来解答:**
A方法在栈顶,此时调用了B方法,会创建B方法的栈帧,此时B方法的栈帧为活动栈帧。若此时B方法中引用了A方法,虚拟机会重新创建一个A方法的活动栈帧。此时的活动栈帧A和之前的A栈帧是独立的,各自享有局部变量表、操作数栈、动态链接、方法出口等信息。
** 递归调用 **
每次重复调用方法是,会创建新的活动栈帧压栈,知道达成某个特定条件后,方法结束调用,并将控制权返回给调用它的方法。
栈指向堆:栈中不会存储成员变量,只会存变量的地址,也就是栈指向堆
讲一下Java堆(重点理解)
- java虚拟机中内存分配最大的一块区域,用来存放实例化的对象和数组,被所有的线程共享。在虚拟机创建时创建,唯一作用就是存放对象实例
- 根据JVM虚拟机的规范,所有的对象个数组对象都要在堆中分配
- 因为存放了大量的对象实例,所以是GC的主要管理区域
- 从垃圾回收的角度上来划分:新生代、老年代
- 当堆中没有可用的储存空间时,会抛出OutOfMemoryError异常。
解释一下本地方法栈
本地方法栈跟 虚拟机栈类似,只不过里面保存的是native 关键字修饰的方法
讲一下方法区(重点了解)
方法区是线程共享的一块区域,它主要存放已被java虚拟机加载的类信息、静态变量、常量、即时编译后的代码等数据
方法区也被称为non-heap(非堆)当方法区中没有可用的储存空间时,会抛出OutOfMemoryError异常。
知道垃圾回收系统吗?垃圾具体是什么?
- 垃圾:指一些没有被引用的对象,因为这些对象无法被访问,对程序也毫无用处。因此为了保证程序运行时的性能,java虚拟机中会不断地自动的进行GC操作已达到释放空间提高效率的作用
- 垃圾回收系统:Java有一套自己进行垃圾清理的机制,是java的核心(必不可少的一部分)开发人员无需手动进行清理。
堆和栈的区别(了解)
对比 | JVM堆 | JVM栈 |
---|---|---|
物理地址 | 堆的物理地址分配对对象是不连续的。因此性能慢些。在GC的时候也要考虑到不连续的分配,所以有各种算法。比如,标记-消除,复制,标记-压缩,分代(即新生代使用复制算法,老年代使用标记——压缩) | 栈使用的是数据结构中的栈,先进后出的原则,物理地址分配是连续的。所以性能快。 |
内存分别 | 堆因为是不连续的,所以分配的内存是在运行期确认的,因此大小不固定。一般堆大小远远大于栈。 | 堆因为是不连续的,所以分配的内存是在运行期确认的,因此大小不固定。一般堆大小远远大于栈。 |
存放的内容 | 堆存放的是对象的实例和数组。因此该区更关注的是数据的存储 | 栈存放:局部变量,操作数栈,返回结果。该区更关注的是程序方法的执行。 |
程序的可见度 | 栈存放:局部变量,操作数栈,返回结果。该区更关注的是程序方法的执行。 | 栈只对于线程是可见的。所以也是线程私有。他的生命周期和线程相同。 |
深拷贝和浅拷贝?(需要知道)
- 浅拷贝:创建一个新的指针,指向原来的内存地址。例如一件衣服分配到了两个人,两个人都指向这一件衣服,衣服破了,对两个人来说都是破的。
- 浅拷贝:创建一个新的指针并申请了一个新的内存空间,这个指针指向了新的内存地址。例如有两件一模一样的衣服给两个人,两个人各自指向自己的衣服,A的衣服破了并不会影响B的衣服。
- 浅复制:仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅复制出来的对象也会相应的改变。
- 深复制:在计算机中开辟一块新的内存地址用于存放复制的对象。
Java中会存在内存泄露吗?说明为什么?(重点理解)
内存泄漏是指没用但是垃圾回收器无法回收的对象,正常情况下java虚拟机会自动的进行垃圾清理。
但即便是这样,也还是会发生内存泄漏,原因:长生命周期的对象持有短生命周期对象的引用,即便短生命周期对象已不再需要,但是因为长生命周期对象的引用导致无法被垃圾回收。
JVM怎样判断对象是否可以被回收?
- 引用计数器算法(已经淘汰):有对象引用时计数器+1,对象引用被释放时计数器-1。对象引用计数器为0时被回收
- 可达性分析算法:从GCRoots开始遍历对象图,遍历过的路径被称为引用链,当找到一个对象跟GCRoots没有任何引用链关联时,这个对象就可以被回收。
怎么避免内存泄漏?(需要知道)
- 当变量被static修饰时,他的生命周期会和应用程序一样长,会一直占用内存空间
- 当试用集合类并且修饰为static时 如果没有对应的删除元素策略,就会导致占用的内存空间只增不减,也需要注意
JVM垃圾回收算法
JVM 垃圾回收算法有哪些?
- 标记-清除算法:标记无用对象,然后进行清除回收。缺点:效率不高,无法清除垃圾碎片。
- 标记-复制算法:按照容量划分二个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块上,然后再把已使用的内存空间一次清理掉。缺点:内存使用率不高,只有原来的一半。
- 标记-整理算法:标记无用对象,让所有存活的对象都向一端移动,然后直接清除掉端边界以外的内存。
- 分代算法:根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代基本采用复制算法,老年代采用标记整理算法。
标记-清除(Mark-Sweep)
“标记-清除”算法,如它的名字一样,算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。之所以说它是最基础的收集算法,是因为后续的收集算法都是基于这种思路并对其缺点进行改进而得到的。
它的主要缺点有两个:
(1)效率问题:标记和清除过程的效率都不高;
(2)空间问题:标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致,碎片过多会导致大对象无法分配到足够的连续内存,从而不得不提前触发GC,甚至Stop The World。
标记-复制(Copying)
为解决效率问题,“复制”收集算法出现了。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
这样使得每次都是对其中的一块进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。
它的主要缺点有两个:
(1)效率问题:在对象存活率较高时,复制操作次数多,效率降低;
(2)空间问题:內存缩小了一半;需要額外空间做分配担保(老年代)
From Survivor, To Survivor使用的就是复制算法。老年代不使用这种算法,
标记-整理(Mark-Compact)
复制收集算法在对象存活率较高时就要执行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。
根据老年代的特点,有人提出了另外一种“标记-整理”(Mark-Compact)算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
分代收集算法(Generational Collection)
GC分代的基本假设:绝大部分对象的生命周期都非常短暂,存活时间短。
“分代收集”算法,把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或“标记-整理”算法来进行回收。
JVM新生代采用的复制算法,老年代采用的整理算法
JDK默认采用Parallel Scavenge(并行)收集器,新生代ParallelGC、老年代ParallelOldGC
目前公司最常用的JDK8:ParNew收集器+CMS分别作用于新生代和老年代搭配使用(下边会有垃圾收集器的介绍)
新生代(young)、老年代(old)、永久代(方法区)
堆的内存模型被分为两个部分:一个是新生代(young),另一个是老年代(old)。默认的内存占比新生代占 1/3的堆空间,老年代占2/3的堆空间,可以通过 “–XX:NewRatio”灵活调整。
新生代又划分成 Eden、Form Survivor、To Survivor 这样划分的目的是为了使 JVM 能够更好的管理堆内存中的对象,包括内存的分配以及回收。
- 新生代中存放短生命周期的对象,每次清理时都会留下少量对象存活,GC垃圾回收时会采用标记-复制的算法。
- 老年代中存放长生命周期的对象,他们存活率高,没有额外的内存空间进行担保、GC垃圾回收时会采用标记-清除、标记-整理
- 永久代就是方法区,用来存放被java虚拟机加载的类信息,静态变量常量等数据,该区域中的数据比老年代中的数据更不容易回收。
Minor GC、Major GC、Full GC分别是什么?
- Minor GC:新生代的gc,发生在新生代(young),新生代对象存活时间短,所以Minor GC非常频繁,一般回收速度也很快(采用标记-复制)。
- Major GC:老年代的gc,发生在老年代(old),通常执行Major GC执行时会连着Minor GC一起执行。Minor GC的速度比 Minor GC的速度要慢得多。(采用标记-清除、标记-整理)
- Full GC:清理整个堆空间,包括新生代和老年代
Minor GC、Major GC、Full GC区别和触发的条件?
Minor GC的触发条件为:
- Eden区满了之后,触发Minor GC。也就是说创建一个新对象时,发现Eden区中不够用,则会触发一次Minor GC。
- 新创建的对象大小 > Eden区剩余的空间。
Major GC和Full GC的触发条件为:
- 每次晋升到老年代的对象平均大小>老年代剩余空间
- Minor GC后存活的对象大小> 超过老年代剩余空间大小
- 永久代空间不足
- 执行System.gc()
- CMS GC出现异常
- 堆内存分配很大的对象
为什么新生代要分成Eden和两个Survivor区域?(重点理解)
-
如果没有survivor区域,Eden区每进行一次Minor GC就会将剩余的对象送到老年代,老年代的空间很快就会被占满,从而导致Major GC或者Full GC的出现
-
Survivor区域存在的意义就是减少被送往老年代的对象,减少Full GC的触发,通过survivor的预筛选保证,只有在新生代中经历过15次Minor GC后还能在新生代中存活的对象,才会被送往老年代。
-
设置两个Survivor区域最大的好处就是解决了**碎片化(内存空间不连续)**第一次创建出来的对象存活在Eden区中,在第一次Minor GC时,Eden区中剩余存活的对象会转移到survivor space s0,Eden被清空。当Eden区再满了,又会触发Minor GC,此时Eden区和s0中剩余存活的对象会被复制到 第二块survivor space s1。
这个过程通过标记-复制算法保证了s1中 来自s0和Eden的对象会使用连续的内存空间。
JVM有哪些垃圾回收器?(了解即可)
垃圾回收器 | 工作区域 | 回收算法 | 工作线程 | 用户线程并行 | 描述 |
---|---|---|---|---|---|
Serial | 新生带 | 复制算法 | 单线程 | 否 | Client模式下默认新生代收集器。简单高效 |
ParNew | 新生带 | 复制算法 | 多线程 | 否 | Serial的多线程版本,Server模式下首选, 可搭配CMS的新生代收集器 |
Parallel Scavenge | 老年带 | 标记-整理 | 单线程 | 否 | 目标是达到可控制的吞吐量 |
Serial Old | 老年带 | 标记-整理 | 多线程 | 否 | Serial老年代版本,给Client模式下的虚拟机使用 |
Parallel Old | 老年带 | 标记-整理 | 多线程 | 否 | Parallel Scavenge老年代版本,吞吐量优先 |
G1 | 新生带 + 老年带 | 标记-整理 + 复制算法 | 多线程 | 是 | JDK1.9默认垃圾收集器 |
- Serial收集器(复制算法): 新生代单线程收集器,标记和清理都是单线程,优点是简单高效;
- ParNew收集器 (复制算法): 新生代收并行集器,实际上是Serial收集器的多线程版本,在多核CPU环境下有着比Serial更好的表现;
- Parallel Scavenge收集器 (复制算法): 新生代并行收集器,追求高吞吐量,高效利用 CPU。吞吐量 = 用户线程时间/(用户线程时间+GC线程时间),高吞吐量可以高效率的利用CPU时间,尽快完成程序的运算任务,适合后台应用等对交互相应要求不高的场景;
- Serial Old收集器 (标记-整理算法): 老年代单线程收集器,Serial收集器的老年代版本;
- Parallel Old收集器 (标记-整理算法):老年代并行收集器,吞吐量优先,Parallel Scavenge收集器的老年代版本;
- CMS(Concurrent Mark Sweep)收集器(标记-清除算法):老年代并行收集器,以获取最短回收停顿时间为目标的收集器,具有高并发、低停顿的特点,追求最短GC回收停顿时间。
- G1(Garbage First)收集器 ( 标记整理 + 复制算法来回收垃圾 ):Java堆并行收集器,G1收集器是JDK1.7提供的一个新收集器,G1收集器基于“标记-整理”算法实现,也就是说不会产生内存碎片。此外,G1收集器不同于之前的收集器的一个重要特点是:G1回收的范围是整个Java堆(包括新生代,老年代),而前六种收集器回收的范围仅限于新生代或老年代。
阐述分代垃圾回收器是怎么工作的?
分代垃圾回收器有两个分区:新生代(young)、老年代(old)
新生代中分为三个分区:Eden、Form survivor、To survivor,他们的比例默认是8:1:1
执行流程:
- Eden 和 Form survivor中存活的对象全部放入 To Survivor中
- 清空Eden 和 Form Survivor
- Form Survivor 和 To Survivor 分区交换,Form Survivor 变成 To Survivor ,To Survivor 变成 Form Survivor
这样每次 从 Form Survivor 到 To Survivor的存货对象都会,年龄就会+1,当年龄达到15(默认配置为15),对象会直接进入到老年代,大对象直接进入到老年代
老年代的空间使用达到某个值时,就会触发全局的垃圾回收,一般使用标记整理算法。以上的操作进行循环往复就是垃圾处理器的执行流程。
大对象是指需要大量连续内存空间的对象。
内存分配策略
首先,java对象的内存通常是在堆上进行分配(随着虚拟机优化技术的提升,后面也会出现在栈上进行分配的)。
对象主要分配在Eden中,如果开启了本地线程缓冲区将会按照线程优先在TLAF(Thread Local Aollection Buffer)上分配。
少数情况下也会在老年代中进行分配,所以说内存分配不是百分百固定的,取决于哪一种垃圾收集器以及虚拟机的相关参数有关。
但是虚拟机还是会遵循以下分配规则:
- 对象优先在Eden区分配
多数情况下,对象都是在新生代中进行分配的。当Eden区没有足够的空间进行分配时,会触发一次Minor GC。如果本次GC 后内存空间还是不足,则会启用内存担保机制,在老年代中分配内存。 - 大对象会直接进入到老年代
大对象是指需要大量连续内存空间的对象。
频分出现大对象是致命的,会导致在还有不少内存空间的情况下提前触发GC用来获取足够的内存空间来安置新对象。
为什么不分配在新生代:因为新生代采用的是标记-复制算法进行垃圾回收,如果将大对象分配到新生代,就会导致Eden区和两个Survivor分区发生大量的复制操作。 - 长期存活的对象将进入老年代
虚拟机采用分代收集思想来管理内存,那么在回收时就回去判断那些对象应该放在新生代,那些对象应该放在老年代。
虚拟机给给每一个对象定义了一个对象年龄的计数器,如果对象在Eden区出生,并且能够被Survivor分区容纳,就被移动到Survivor分区中,此时对象的年龄为1。也就是说每进行一次Minor GC就会将剩余存活的对象在Survivor分区中进行一次移动,而移动一次对象年龄就会+1,当对象年龄达到15(默认)时,会被晋升到老年代。
Java虚拟机的类加载机制
类加载过程
加载=>校验=>准备=>解析=>初始化
- 加载:在硬盘上查找并通过IO读入字节码文件,使用到类时才会加载,例如调用类的main()方法,new对象等等,在加载阶段会在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
- 校验:校验字节码文件的正确性(字节码文件的内容开头 café babe)
- 准备:给类的静态变量分配内存,并赋予默认值(常量会直接赋值 final修饰)
- 解析:将符号(例如:方法名 修饰符等)引用替换为直接引用,该阶段会把一些静态方法(比如main方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态链接过程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接引用
- 初始化:对类的静态变量初始化为指定的值,执行静态代码块
描述JVM类加载原理
Java中所有的类都需要由类加载器将类装载到虚拟机内存中才能运行,类加载器本身也是类,他的工作就是将class文件加载到内存中。
类加载器的装载是动态的,它不会一次性将所有的class文件都加载到虚拟机内存中,它会保证程序能够运行的基础类进行完全加载,其它类在使用到时进行加载。
类装载方式:
- 隐式加载:程序在运行时遇到了通过new()方式创建的对象时,隐式调用类装载器将对应类加载到jvm中。
- 显示加载:通过反射的方式Class. forname() 等显示的加载需要的类。
什么是类加载器?都有哪些类加载器?
类加载器:将字节码文件中的内容加载到jvm中。
- 启动类加载器Bootstrap ClassLoader(引导类加载器):
- 使用C++语言实现,嵌套在JVM内部
- 用来加载java的核心类库(JAVA HOME/jre/lib/rt.jar.resources.jar或sun.boot.class. path路径下的内容)
- 扩展类加载器(Extension ClassLoader):
- java语言编写
- 应用类加载器(自定义加载器)
- java语言编写
讲一下双亲委派机制
子类加载器接收到类加载请求时,不会自己先去加载,而是会将加载请求委托给父类加载器,一直到最顶层的启动类加载器,当启动类加载器加载不了时(它的搜索范围没有找到所需的类),子类加载器才会尝试去进行加载。
子类加载器会将加载请求交于父类加载器,父类加载器完成加载则子类加载器不需要进行加载。父类加载器无法加载类时,子类加载器会去加载类。
沙箱安全机制了解过吗?
沙箱安全机制设计出来是用来保护核心类库不会被随意修改。原理就是核心类只会被加载一次,比如我们自定义java.lang.String类并不会覆盖掉核心类库的String类。
保证了一个类只会被加载一次,父类加载后子类不会再加载。
JVM调优
JVM 调优的参数可以在那设置参数值
- 可以在IDEA,Eclipse,工具里设置
- 如果上线了是WAR包的话可以在Tomcat设置
- 如果是Jar包直接 :java -jar 是直接插入JVM命令就好了
java -Xms1024m -Xmx1024m ...等等等 JVM参数 -jar springboot_app.jar &
说一下 JVM 调优的工具?
- jconsole:用于对 JVM 中的内存、线程和类等进行监控;
- jvisualvm:JDK 自带的全能分析工具,可以分析:内存快照、线程快照、程序死锁、监控内存的变化、gc 变化等。
常用的 JVM 调优的参数都有哪些?
#常用的设置
-Xms:初始堆大小,JVM 启动的时候,给定堆空间大小。
-Xmx:最大堆大小,JVM 运行过程中,如果初始堆空间不足的时候,最大可以扩展到多少。
-Xmn:设置堆中年轻代大小。整个堆大小=年轻代大小+年老代大小+持久代大小。
-XX:NewSize=n 设置年轻代初始化大小大小
-XX:MaxNewSize=n 设置年轻代最大值
-XX:NewRatio=n 设置年轻代和年老代的比值。如: -XX:NewRatio=3,表示年轻代与年老代比值为 1:3,年轻代占整个年轻代+年老代和的 1/4
-XX:SurvivorRatio=n 年轻代中 Eden 区与两个 Survivor 区的比值。注意 Survivor 区有两个。8表示两个Survivor :eden=2:8 ,即一个Survivor占年轻代的1/10,默认就为8
-Xss:设置每个线程的堆栈大小。JDK5后每个线程 Java 栈大小为 1M,以前每个线程堆栈大小为 256K。
-XX:ThreadStackSize=n 线程堆栈大小
-XX:PermSize=n 设置持久代初始值
-XX:MaxPermSize=n 设置持久代大小
-XX:MaxTenuringThreshold=n 设置年轻带垃圾对象最大年龄。如果设置为 0 的话,则年轻代对象不经过 Survivor 区,直接进入年老代。
#下面是一些不常用的
-XX:LargePageSizeInBytes=n 设置堆内存的内存页大小
-XX:+UseFastAccessorMethods 优化原始类型的getter方法性能
-XX:+DisableExplicitGC 禁止在运行期显式地调用System.gc(),默认启用
-XX:+AggressiveOpts 是否启用JVM开发团队最新的调优成果。例如编译优化,偏向锁,并行年老代收集等,jdk6纸之后默认启动
-XX:+UseBiasedLocking 是否启用偏向锁,JDK6默认启用
-Xnoclassgc 是否禁用垃圾回收
-XX:+UseThreadPriorities 使用本地线程的优先级,默认启用
等等等......
JVM的GC收集器设置
-
-xx:+Use xxx GC
- xxx 代表垃圾收集器名称
-XX:+UseSerialGC:设置串行收集器,年轻带收集器
-XX:+UseParNewGC:设置年轻代为并行收集。可与 CMS 收集同时使用。JDK5.0 以上,JVM 会根据系统配置自行设置,所以无需再设置此值。
-XX:+UseParallelGC:设置并行收集器,目标是目标是达到可控制的吞吐量
-XX:+UseParallelOldGC:设置并行年老代收集器,JDK6.0 支持对年老代并行收集。
-XX:+UseConcMarkSweepGC:设置年老代并发收集器
-XX:+UseG1GC:设置 G1 收集器,JDK1.9默认垃圾收集器