文章目录
JVM内存
JVM中的内存主要划分为5个区域,即方法区,堆内存,程序计数器,虚拟机栈以及本地方法栈
线程共享
方法区
- 主要用于存储已被虚拟机加载的类信息,也可以称为==“永久代”,线程之间共享的区域,用来存储常量,静态变量以及JIT编译后的代码==,垃圾回收效果一般,通过==-XX:MaxPermSize==控制上限
- 永久带的内存回收的主要目标是针对常量池的回收和类型的卸载, 因此收益一般很小
- 运行时常量池(Runtime Constant Pool)是方法区的一部分,Class 文件中除了有类的版 本、字段、方法、接口等描述等信息外,还有一项信息是常量池
- 用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中
堆内存
堆内存是垃圾回收的主要场所,也是线程之间共享的区域,主要用来存储创建的对象实例,通过-Xmx==和-Xms 可以控制大小
线程私有
虚拟机栈(栈内存)
- 栈内存中主要保存局部变量、基本数据类型变量以及堆内存中某个对象的引用变量
- 每个方法在执行的同时都会创建一个=栈帧(Stack Frame)用于存储局部变量表,操作数栈,动态链接,方法出口等信息。栈中的栈帧随着方法的进入和退出有条不紊的执行着出栈和入栈的操作
程序计数器:
- 当前线程执行的字节码的位置指示器
- 字节码解释器工作时就是通过计数器来选取下一条需要执行的字节码指令
- 是内存区域中唯一一个在虚拟机规范中没有规定任何OutOfMemoryError情况的区域
本地方法栈
主要是为JVM提供native 方法的服务
JVM 运行时内存
Java 堆从 GC 的角度还可以细分为: 新生代(Eden 区、From Survivor 区和 To Survivor 区)和老年代
新生代
是用来存放新生的对象。一般占据堆的 1/3 空间。由于频繁创建对象,所以新生代会频繁触发MinorGC 进行垃圾回收。新生代又分为 Eden 区、ServivorFrom、ServivorTo 三个区
Eden 区
Java 新对象的出生地(如果新创建的对象占用内存很大,则直接分配到老年代)。当 Eden 区内存不够的时候就会触发 MinorGC,对新生代区进行一次垃圾回收。
ServivorFrom
上一次 GC 的幸存者,作为这一次 GC 的被扫描者。
ServivorTo
保留了一次 MinorGC 过程中的幸存者。
MinorGC
MinorGC过程(复制->清空->互换),MinorGC 采用复制算法。
-
eden、servicorFrom 复制到 ServicorTo,年龄+1
- 把Eden 和 ServivorFrom 区域中存活的对象复制到 ServicorTo 区域(如果有对象的年龄以及达到了老年的标准,则赋值到老年代区),同时把这些对象的年龄+1(如果 ServicorTo 不够位置了就放到老年区)
-
清空 eden、servicorFrom
清空 Eden 和 ServicorFrom 中的对象; -
ServicorTo 和 ServicorFrom 互换
ServicorTo 和 ServicorFrom 互换,原 ServicorTo 成为下一次 GC 时的 ServicorFrom区
S0区
即survivor from区,也叫from区
S1区
即survivor to区,也叫to区
老年代
-
老年代的对象比较稳定,所以 MajorGC 不会频繁执行,在进行 MajorGC 前一般都先进行 了一次 MinorGC,使得有新生代的对象晋身入老年代,导致空间不够用时才触发
-
当无法找到足够大的连续空间分配给新创建的较大对象时也会提前触发一次 MajorGC 进行垃圾回收腾出空间
-
MajorGC 采用标记清除算法
- 首先扫描一次所有老年代,标记出存活的对象
- 然后回收没有标记的对象
- MajorGC 的耗时比较长(先扫描再回收),MajorGC 会产生内存碎片为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。当老年代也满了装不下的时候,就会抛出 OOM(Out of Memory)异常。
永久代
指内存的永久保存区域,主要存放 Class 和 Meta(元数据)的信息,Class 在被加载的时候被放入永久区域,它和和存放实例的区域不同,GC 不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的 Class 的增多而胀满,最终抛出 OOM 异常。
JAVA8 与元数据
- 在 Java8 中,永久代已经被移除,被“元数据区”(元空间)的区域所取代
- 元空间的本质和永久代类似,元空间与永久代之间最大的区别在于:元空间不在虚拟机中而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制
- 类的元数据放入 native memory, 字符串池和类的静态变量放入 java 堆中,这样可以加载多少类的元数据就不再由 MaxPermSize 控制, 而由系统的实际可用空间来控制
垃圾回收GC
如何确定垃圾
引用计数法
- 在 Java 中,引用和对象是有关联的。如果要操作对象则必须用引用进行。可以通过引用计数来判断一个对象是否可以回收,即一个对象若无任何与之关联的引用,即引用计数为 0,则说明对象不太可能再被用到,就是可回收对象
- 无法解决引用计数的循环引用问题
可达性分析
- 通过一系列的“GC roots”对象作为起点搜索,如果在“GC roots”和一个对象之间没有可达路径,则称该对象是不可达的
- 不可达对象变为可回收对象至少要经过两次标记过程。两次标记后仍然是可回收对象,则将面临回收
可以作为GCRoots的对象
- 虚拟机栈中的引用对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI引用的对象
如何确定一个常量是废弃常量
运行时常量池中的常量字符串若无任何String类对象引用则说明是废弃常量,有必要时会被清理出常量池
如何判断一个类为无用类
GC算法
标记清除算法(Mark-Sweep)
最基础的垃圾回收算法,分为两个阶段,标注和清除,高算法会导致内存碎片化严重,后续大对象可能无法找到连续空间
- 标记阶段标记出所有需要回收的对象
- 清除阶段回收被标记的对象所占用的空间
复制算法
按内存容量将内存划分为等大小的两块。每次只使用其中一块,当这一块内存满后将尚存活的对象复制到另一块上去,把已使用的内存清掉
- 为了解决 Mark-Sweep 算法内存碎片化的缺陷而被提出的算法
- 实现简单,内存效率高,不易产生碎片,但是内存被压缩到了原本的一半,且存活对象增多的话,Copying 算法的效率会大大降低
标记整理算法
- 标记阶段和 Mark-Sweep 算法相同
- 标记后不是清理对象,而是将存活对象移向内存的一端。然后清除端边界外的对象
分代收集算法
分代收集法是目前大部分 JVM 所采用的方法,其核心思想是根据对象存活的不同生命周期将内存划分为不同的域,一般情况下将 GC 堆划分为老生代(Tenured/Old Generation)和新生代(Young Generation)。老生代的特点是每次垃圾回收时只有少量对象需要被回收,新生代的特点是每次垃 圾回收时都有大量垃圾需要被回收,因此可以根据不同区域选择不同的算法
新生代与复制算法
- 目前大部分 JVM 的 GC 对于新生代都采取 Copying 算法,因为新生代中每次垃圾回收都要回收大部分对象,即要复制的操作比较少
- 通常并不是按照 1:1 来划分新生代,一般将新生代 划分为一块较大的 Eden 空间和两个较小的 Survivor 空间(From Space, To Space)
- 每次使用每次使用Eden 空间和其中的一块 Survivor 空间,当进行回收时,将该两块空间中还存活的对象复制到另 一块 Survivor 空间中
老年代与标记整理算法
老年代因为每次只回收少量对象,因而采用 Mark-Compact 算法
- JAVA 虚拟机提到过的处于方法区的永生代(Permanet Generation),它用来存储 class 类,常量,方法描述等。对永生代的回收主要包括废弃常量和无用的类。
- 对象的内存分配主要在新生代的 Eden Space 和 Survivor Space 的 From Space(Survivor目前存放对象的那一块),少数情况会直接分配到老生代。
- 当新生代的 Eden Space 和 From Space 空间不足时就会发生一次 GC,进行 GC 后,Eden Space 和 From Space 区的存活对象会被挪到 To Space,然后将 Eden Space 和 From Space 进行清理
- 如果 To Space 无法足够存储某个对象,则将这个对象存储到老生代。
- 在进行 GC 后,使用的便是 Eden Space 和 To Space 了,如此反复循环。
- 当对象在 Survivor 区躲过一次 GC 后,其年龄就会+1。默认情况下年龄到达 15 的对象会被 移到老生代中
GC分代收集算法 和分区收集算法
分代收集算法
- 当前主流 JVM 垃圾收集都采用”分代收集”(Generational Collection)算法,
- 这种算法会根据 对象存活周期的不同将内存划分为几块, 如 JVM 中的 新生代、老年代、永久代,这样就可以根据 各年代特点分别采用最适当的 GC 算法,在新生代-复制算法
在老年代-标记整理算法。
分区算法
则将整个堆空间划分为连续的不同小区间, 每个小区间独立使用, 独立回收. 这样做的好处是可以控制一次回收多少个小区间
强引用,软引用,弱引用,虚引用
强引用
默认,最常见的引用,一个对象赋给强引用变量,则永远都不会被JVM回收,即使出现OOM这也是JAVA内存泄露的主要原因之一
对于一个普通对象,如果没有其他引用关系,只要超过了引用的作用域或者显式将强引用置为nul则可以被GC
Object obj1 = new Object();//这样定义即为强引用
Object obj2 = obj1;//引用赋值,也为强引用
obj1 = null;
System.gc();
System.out.println(obj2);
//obj2能正常打印,即没有被gc
//obj1被GC
软引用
内存充足时不会被GC,内存不足时会被GC
弱引用
垃圾回收机制运行,无论JVM内存空间是否足够都会回收在高速缓存中使用到了软引用和弱引用
虚引用
如果一个对象仅有虚引用,和没有引用一样,任何时候都可被回收,必须和引用队列联合使用。
主要作用是跟踪对象被垃圾回收的状态,当程序发现某个虚引用加入引用队列,就可以在该对象被回收前采取必要措施,相当于通知机制。
引用队列
创建引用的时候可以指定关联的队列,GC释放对象内存时,会将引用加入引用队列,相当于一个通知机制
GC垃圾回收算法与垃圾收集器
查看服务器默认的垃圾回收器
在IDEA的Terminal中,java -XX:+PrintCommandLineFlags -version
输出的最后一个参数为 -XX:+UserParrelGC。(目前用的是Java8
默认配置)
默认的垃圾收集器有哪些
SerialGC ParallelGC ConcMarkSweepGC
ParNewGC == ParallelOldGC== G1GC
(SericalOldGC已被淘汰)
SerialGC(单线程+复制算法)
-
单线程收集器,进行GC时暂停所有用户线程,最稳定效率最高,时间开销大
-
对应JVM参数:-XX:+UseSerialGC
-
开启后会使用:==Serial(Young区用)+SerialOld(Old区用)==的收集器组合 ,并且新生代使用复制算法,老年代使用标记整理算法
-
配置时:== -Xms 10m -Xmx 10m -XX:PrintGCDetails -XX:+PrintCommandLineFlags -XX: UseSerialGC ==
Parallel(Paraller Scavenge多线程+复制算法)
-
SerialGC的多线程版, 吞吐量优先级垃圾收集器,串行收集器在新生代和老年的并行化
-
吞吐量=允许用户代码时间/(运行用户代码时间+GCSHIJIAN ),高吞吐量意味着高效利用CPU,多用于在后台运算而不需要太多交互的任务
-
自适应调节策略
-
对应JVM参数:-XX:+UseParallelGC或-XX:+UseParallelOldGC(可相互激活)
开启后新生代用复制算法,老年代用标记整理算法
开启后会使用:==Parallel(Young区用)+ParallellOld(老年代)==的收集器组合 -
配置时:== -Xms 10m -Xmx10m -XX:PrintGCDetails -XX:+PrintCommandLineFlags -XX: ParallelGC ==
SerialOld(单线程+标记整理算法)
老年代Serial收集器 ,已经弃用
ParallelOld(多线程+标记整理算法)
-
为了在老年代同样提供吞吐量优先的垃圾收集器
-
对应JVM参数:XX:+UseParallelGCXX:+UseParallelOldGC(可相互激活)
开启后新生代用复制算法,老年代用标记整理算法
开启后会使用:==Parallel(Young区用)+ParallellOld(老年代)==的收集器组合 -
配置时:== -Xms 10m -Xmx10m -XX:PrintGCDetails -XX:+PrintCommandLineFlags -XX: ParallelOldGC ==
ParNew(多线程+复制算法)
-
并行收集器,使用多线程进行垃圾回收,进行GC时会STW(stop the world)暂停其他工作现场直至GC结束
-
ParNew收集器就是Serial收集器新生代的并行多线程版,最常用的应用场合是配合老年代的CMS GC
对应JVM参数:-XX:+UseParNewGC (启用ParNew只影响新生代手机,不影响老年代)
开启后会使用:==ParNew(Young区用)+SerialOld(老年代)==的收集器组合
开启后新生代用复制算法,老年代用标记整理算法
配置时:== -Xms 10m -Xmx10m -XX:PrintGCDetails -XX:+PrintCommandLineFlags -XX: ParNewGC ==
CMS(多线程标记清除算法)
-
并发标记清除回收器,是一种获得最短回收停顿时间为目标的收集器,并发(与用户线程一起执行)收集低停顿
-
对应JVM参数: XX:+UseConcMarkSweepGC,开启该参数后会自动将-XX:+UseParnewGC打开 ,新生代用复制算法,老生代用标记清理算法
开启后会使用: ParNew(Young区)+CMS(old区)+SerialOld收集器组合(SerialOld作为CMS出错的后备收集器)
G1(标记整理算法)
- 面向服务端应用的收集器,应用于多处理器和大容量内存环境中,实现高吞吐量的同时尽可能满足垃圾收集暂停时间的要求
- G1只有逻辑上的分代概念,整个内存分区不存在物理上的老年代年轻代也不需要独立的survivor堆做复制准备
-优点
- 利用多CPU多核硬件环境,尽量缩短了STW
- 可以非常精确控制停顿时间,在不牺牲吞吐量前提下,实现低停顿垃圾回收
- 整体上采用了标记整理算法,局部采用复制算法,不会产生内存碎片
- 避免了全局扫描,只需要按照区域来扫描
与CMS相比的优势
- G1不会产生内存碎片
- 可以精确控制停顿
如何选择垃圾收集器
-
Young Gen:UseSerialGC UseParallelGC UseParNewGC
Old Gen: UseSericalOldGC UseParallelOldGC UseConcMarkSweepGC
UseG1GC 均适用 -
单CPU或小内存,单机程序
-XX+UserSerialGC -
多CPU下,需要最大吞吐量,允许小停顿
-XX+UseParallelGC
-多CPU,追求低停顿时间
-XX+UseConcMarkSweepGC
-XX+ParnewGC
谈谈OOM
StackOverflowError和OutOfMemoryError属于Error
java.lang.StackOverflowError
属于Error,一般深度的方法调用会导致sofe
java.lang.OutOfMemoryError:java heap space
堆内存溢出
java.lang.OutOfMemoryError:Metaspace
元空间溢出
java.lang.OutOfMemoryError:GC overhead limit exceeded
GC时间过长(超过98%的时间用以GC并且回收了不到2%的堆内存)会抛出此错误
不抛出该错误,会导致GC清理的内存很快再次被填满迫使GC再次执行,恶性循环,CPU的使用率一直100%而GC毫无成果。
java.lang.OutOfMemoryError:Direct buffer memory
NIO引起的异常
java.lang.OutOfMemoryError:unable to create new native thread
高并发请求服务器时经常出现,导致原因:
- 一个应用进程中创建了太多线程,超过系统承载上限
- 服务器并不允许你的应用程序创建这么多线程,linux默认允许单个进程创建线程数为1024(ulimit -u 可以查看默认线程数,)
解决方法:降低创建线程数,修改linux服务器配置,扩大linux默认限制
Java类加载机制
指虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行验证、准备、解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型
类从被加载到虚拟机内存中到卸载出内存,生命周期包括:加载、验证、准备、解析、初始化、使用、卸载七个阶段。类加载机制的保持则包括前面五个阶段。
加载
- 指将类的.class文件中的二进制数据读入到内存中,放在方法区
- 然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。
验证
验证的作用是确保被加载的类的正确,包括文件格式验证,元数据验证,字节码验证以及符号引用验证。
准备
为类的静态变量(方法区中变量)分配内存,并将其初始化为默认值
解析
将类中符号引用转换为直接引用
符号引用
符号引用与虚拟机实现的布局无关,引用的目标并不一定要已经加载到内存中。各种虚拟机实现的内存布局可以各不相同,但是它们能接受的符号引用必须是一致的,因为符号引用的字面量形式明确定义在 Java 虚拟机规范的 Class 文件格式中
直接引用
直接引用可以是指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄。如果有了直接引用,那引用的目标必定已经在内存中存在
初始化
- 初始化阶段为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化
- 初始化阶段是执行类构造器方法的过程。方法是由编译器自动收集类中的类变量的赋值操作和静态语句块中的语句合并而成的。虚拟机会保证子方法执行之前,父类的方法已经执行完毕,如果一个类中没有对静态变量赋值也没有静态语句块,那么编译 器可以不为这个类生成()方法。
对象创建过程中的内存分配
对象创建过程
一般通过new指令来创建对象,当虚拟机遇到一条new指令的时候,会去检查这个指令的参数是否能在常量池中定位到某个类的符号引用,并且检查这个符号引用代表的类是否已经被加载,解析和初始化。如果没有,那么会执行类加载过程。
通过执行类的加载,验证,准备,解析,初始化步骤,完成了类的加载,这个时候会为该对象进行内存分配,把一块确定大小的内存从Java堆中划分出来,在其上完成对象的创建工作。
对象内存分配方式
对象的内存分配有两种方式,即指针碰撞和空闲列表方式。
- 指针碰撞方式
假设Java堆中的内存是绝对规整的,用过的内存在一边,未使用的内存在另一边,中间有一个指示指针,那么所有的内存分配就是把指针向空闲空间那边挪动一段与对象大小相等的距离。
- 空闲列表方式
如果Java堆内存中不是规整的,已使用和未使用的内存相互交错,那么虚拟机就必须维护一个列表用来记录哪块内存是可用,在分配时找到一块足够空间分配对象实例,并且需要更新列表上的记录。
(Java 堆内存是否规整是由所使用的垃圾收集器是否拥有压缩整理功能来决定的)
内存的分配如何保证线程安全呢?
- 对分配内存空间的动作进行同步处理,通过“CAS + 失败重试”的方式保证更新指针操作的原子性
- 把分配内存的动作按照线程划分在不同的空间之中,即给每一个线程都预先分配一小段的内存,称为本地线程分配缓存(TLAB),只有TLAB用完并分配新的TLAB时,才需要进行同步锁定。 虚拟机是否使用TLAB,可以通过-XX: +/-UserTLAB参数来设定
对象被访问的时候是怎么被找到的?
当创建一个对象的时候,在栈内存中会有一个引用变量,指向堆内存中的某个具体的对象实例。
目前常见的对象访问方式有两种,即句柄访问方式和直接指针访问方式,
-
句柄访问方式:
在JVM的堆内存中划分出一块内存来作为句柄池,引用变量中存储的就是对象的句柄地址,句柄中包含了对象实例数据与类型数据各自的具体地址信息,句柄地址方式不直接,访问速度较慢。 -
直接指针访问方式:
引用变量中存储的就是对象的直接地址,通过指针直接访问对象。节省了一次指针定位的时间开销,速度较快。Sun HotSpot使用了直接指针方式进行对象的访问。
类加载器
类加载器分类
启动类加载类(BootstrapClassLoader)
负责加载存放在JDK\jre\lib下,或被-Xbootclasspath参数指定的路径中的类。
扩展类加载(ExtClassLoader)
扩展类加载器负责加载JDK\jre\lib\ext目录中,或者由java.ext.dirs系统变量指定的路径中的所有类库(如javax.*开头的类)
应用类加载器(AppClassLoader)
负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器
类加载器的职责:
全盘负责
当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显式使用另外一个类加载器来载入。
父类委托(双亲委托机制)
类加载机制会先让父类加载器试图加载该类,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。
父类委托机制是为了防止内存中出现多份同样的字节码,污染源代码保证java程序安全,沙箱安全。
缓存机制:
保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,先从缓存区寻找该Class,只有缓存区不存在,系统才会加载该类,并存入缓存区(因此修改了Class后,必须重启JVM,程序的修改才会生效)
JDK8中在内存管理上的变化
JDK8中出现了元空间代替了永久代。两者都是对JVM规范中方法区的实现。区别在于元空间使用本地内存,默认元空间的大小仅受本地内存限制,也可以通过-XX:MetaspaceSize指定元空间大小。
为什么要使用元空间代替永久代?
字符串在永久代中,容易出现性能问题和内存溢出的问题。类和方法的信息等比较难确定大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,使用元空间则使用了本地内存不容易溢出
查看JVM系统默认值
JVM参数类型
- 标配参数(Java -version java -help)
- X参数
- XX参数
JVMXX参数
Boolean类型
-xx: +或者-某个属性值(+表示开启,-表示关闭)
- Boolean类型
- 如何在IDEA中添加JVM运行参数: Run/EditConfigurations/Application/找到对应进程/VM options,添加参数即可
- 如何查看一个正在运行的java程序它的某个JVM参数是否开启?具体值为多少?
jps(查看java进程编号)(位于JDK/bin/下的jps.exe)
在IDEA的Terminal下 : jps -l (效果与在win下终端运行一样)
可以得到后台进程的进程号等
jinfo(查看正在运行的各种java进程各种信息)
jinfo -flag PrintGCDetails 13532 (查看进程13532是否开启打印进程回收细节)
输出:-XX:-PrintGCDetails(没有配置该参数)
KV设值类型
-XX:属性key=属性值value
- KV类型
- jps -l得到进程号(4300)
- jinfo -flag MetaspaceSize 4300
(jinfo -flags 4300可以打印出所有参数)
输出为: -XX:MetaspaceSize=21807104
(默认为2180710) - Run/Configurations/Applications/对应进程//Configuration/VM options 配置 -XX:MetaSpaceSize=1024m
重新按照上述步骤查询该参数的值
两个经典参数-Xms和-Xmx
属于XX参数
- -Xms等价于-XX:InitialHeapSize 初始堆空间大小
- -Xmx等价于-XX:MaxHeapSize 最大堆空间大小
设置一样
- 避免JVM在运行过程中向OS申请
- 延后启动后首次GC的发生时机
- 减少启动初期的GC次数
- 尽可能避免使用swap space
查看JVM初始默认值的方法
第一种
jps -l
jinfo -flag 具体参数 java进程号
jinfo -flags java进程号
第二种
-
直接在终端下 :
java -XX:+PrintFlagsInitial
(一般会使用java -XX:+PrintFlagsFinal -version)
(java-XX:PrintCommandLineFlags 最关键最后一个参数,查看本JVM使用的垃圾回收器版本) -
打印出的参数中,=的表示未被修改过,若:=则表示被人为或JVM修改过
- 在java程序运行时修改JVM参数
java -X:PrintFlagsFinal -Xss128K T (T表示运行的java类名字)
java -XX:+PrintFlagsFina -XX:MetaspaceSize -version T(T表示测试程序)
java -XX:+PrintFlagsFina -XX:MetaspaceSize=512m T 表示该参数使用该更新值
- 在java程序运行时修改JVM参数
JVM常用基本配置参数
- -Xms(初始堆最小内存,-XX:InitialHeapSize,默认1/16)
- -Xmx(初始最大内存,-XX:MaxHeapSize,默认1/4) 要配置成一样
- -Xss(设置单个线程栈的大小,-XX:ThreadStackSize,一般默认为512k~1024k,在IDAE查看该参数为0即使用默认值)
- -Xmn (设置年轻代大小,一般不调整)
- -XX:MetaspaceSize(设置元空间大小,元空间和永久代是不同斑斑JDK JVM对方法区的实现,最大的区别是,元空间使用的是本地内存, 受本地内存限制,元空间默认20多M,无论物理内存多大)
- -XX:PrintGCDetails输出详细GC收集日志信息
MinorGC和FUllGC GCs 收集日志信息 - -XX:SurvivorRatio设置新生代中eden和S0/S1空间d的比例
默认
-XX:SurvivorRatio=8,Eden:S0:S1=8:1:1 - -XX:NewRatio 配置年轻代与老年代在堆结构的占比
- -XX:MaxTenuringThreshold设置垃圾最大年龄
如何配置参数
在Run/Configurations/Applications/对应进程/Configuration/VM options 配置参数
如- XX:+PrintGCDetails