这里写目录标题
深入理解JAVA虚拟机
一、了解历史
//JDK(200M)、JRE、JVM
JDK(java开发工具集合)包含JRE(java运行时环境)和Java开发工具包。
JRE包含JVM(java虚拟机)和Java核心类库。JRE包含JAVA api和 JVM
// 什么是JVM?(C 和 C++ 也是跨平台的)
我们写一个HellO.java 文件,最终会编译成Hello.class字节码文件。这个字节码文件可以在windows环境运行,可以在Linux环境运行。就是我们在不同的平台都安装对应该平台的 JVM,从.java源文件到.class 字节码文件的过程就是JVM的工作
内存溢出场景
public class 堆内存溢出 {
public static void main(String[] args) {
List<demo> demolist =new ArrayList<demo>();
while (true){
demolist.add(new demo());
}
}
}
// 内存会一直增加 报错:java.lang.OutOfMemoryError 堆内存溢出
1、用完不释放 2、gc回收的速度赶不上新增的次数
//什么情况回抛出OOM?
堆内存溢出、方法区溢出、直接内存不足
JVM可视化监控工具
JDK 1.8的新特性
Lambda 表达式
HashMap 的查询优化
方法区的优化
stream流 函数式编程
Date API
JVM的垃圾回收机制
// java 比 C 、C++ 最强大的地方 就是内存管理(不需要申请内存、释放内存,这些都由Java虚拟机来替我完成)
在 java 中,程序员是不需要手动的去释放一个对象的内存的,而是由虚拟机自行执行。在JVM 中,有一个垃圾回收线程,它是低优先级的,在正常情况下是不会执行的,只有在虚拟机空闲或者当前堆内存不足时,才会触发执行,扫描那些没有被任何引用的对象,并将它们添加到要回收的集合中,进行回收。
四个概念:
class文件:编译后生成的文件,存在硬盘上
class content:cpu执行任何程序不可能从硬盘中加载,都是从内存中加载的,加载阶段,将class文件加载进内存。就是一个流。存放在直接内存(操作系统内存)
Class对象:user.class;这个对象,存在堆区
对象:new 生成的对象,在堆区
//类加载子系统加载一个class文件在内存中是如何存储的?
会生成一个instanceklass:存在方法区,有常量池、元信息、字段信息等
还有instanceMirroklass: 镜像类,Class对象就是镜像类的实例。存在堆区
//方法区、永久代、元空间的关系:
方法区是规范,永久代、元空间是具体的实现。就类似于方法区是接口,永久代和元空间是实现类
永久代是JDK1.7的实现和叫法,元空间是JDK1.8的实现和叫法
1.7永久代是放在堆区的,1.8元空间是直接放在操作系统内存的
为什么有这种改变? 1、gc问题:堆区是用来存放对象的,又要存放类的元信息,而类的元信息有很少改变,就把它单独提出来放在直接内存中了。增大堆内存,减少gc次数。
------分------割------线------
一个类执行,1、调用了.javac 编译这个程序,生成.class文件。
2、调用java命令运行这个java程序
运行就是jvm的类加载子系统把.class文件加载进内存中,生成class content,解析后生成instanceklass 放在方法区,生成了instanceMirroklass对象,放在堆里,所以Class对象是在堆里的。虚拟机栈,是栈的一种应用。jvm有几个虚拟机栈?一个线程一个。一个虚拟机栈有几个栈帧?调用方法的次数,调用一次生成一个栈帧。栈帧是一种很小的数据结构。栈帧的组成:局部变量表、操作数栈、动态链接 直接地址、返回地址 恢复现场以及附加信息等。
动态链接:方法对象的内存地址,一个方法对应一个方法对象,是在方法区,直接地址就是说存的是内存的直接地址。
jvm执行一个方法的流程(add方法),main方法里面包含add方法。1、创建栈帧 2、保存main方法的程序计数器 3、修改线程的局部变量表开始指针为add方法的 4、修改线程的操作数栈当前指针为add方法的 当add方法执行完了,add方法的执行完了,1、修改线程的操作数栈为main方法的 2、 修改线程的局部变量表为main方法的 3、恢复程序计数器 4、判断有没有返回值,有-->压入main方法的操作数栈 5、释放栈帧 这就是恢复现场了
返回地址 保存现场:执行引擎把add方法执行完了,要返回到main方法,那我程序计数器执行到哪里了、局部变量表和操作数栈是什么情况,这些都是在返回地址里面保存的。这就是恢复现场的意思.
new 对象:首先在堆区生成一个对象,将对象的引用压入栈,复制栈顶数据,再次压入栈(this指针在这里赋值),执行对象的构造方法,pop出栈顶数据,保存到index=1的位置(局部变量表)这里完成赋值。这里是有严格的顺序的,所以new一个对象要加volatile修饰符
------分------割------线------
堆:
什么对象会进入老年代?
1、gc次数超过15次的,除了我理解的那个外还有 gc的年龄栈4个bit,就是2的4次方,0-15
2、空间担保(伊甸园区gc后,from区装不下,这个时候就放入老年代)
3、动态年龄判断(为了能更好地适应不同程度的内存状况,虚拟机并不是永远地要求对象的年龄必须达到了MaxTenuringThreshold才能晋升到老年代,年龄 1 到 N 的对象大小超过 Survivor 的 50% 时,则将大于等于年龄 N 的对象放入老年代。书里面的意思是,(同龄对象大小超过 50% 时,则将大于等于该年龄的对象放入老年代。因为如果是同龄对象的话,那么这批放入老年代的对象就必然是 1 岁,因为如果在 1 岁的时候没有达到 50%,那么在之后就更不可能达到 50% 了。)所以是我的那个理解。
4、大对象(对象大小超过Eden区一半,大小是动态计算的)
二、内存结构
jvm 虚拟机 可以看成三部分
1、JVM运行时数据区(这个重要、也是平时知道的最多的)
2、类加载子系统
3、执行引擎:
jvm里面有两套执行引擎:1、字节码解释器(java字节码-->c++-->机器码)
2、模板解释器(java字节码-->机器码)
首先通过类加载器(ClassLoader)会把 Java 代码转换成字节码,运行时数据区再把字节码加载到内存中,而字节码文件只是 JVM 的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的命令解析器执行引擎,将字节码翻译成底层系统指令,再交由 CPU 去执行,而这个过程中需要调用其他语言的本地库接口来实现整个程序的功能。
运行时数据区:
线程共享区:(线程不安全,任何一个线程都可以操作)
方法区:存储运行时常量池,类信息,常量,静态常量等
java堆:存储对象实例
线程独占区:
栈(虚拟机栈):存储运行时所需要是数据,局部变量表等
本地方法栈:为JVM所调用的本地方法服务
程序计数器:记录当前线程所执行到的字节码的行号,或者是说索引
五大部分
程序计数器,不会有异常
虚拟机栈、本地方法栈、Java堆、方法去:会有异常
程序计数器、虚拟机栈、本地方法栈:不会有GC
JAVA堆、方法去:会有GC
1、虚拟机栈
虚拟机栈描述的是java方法执行的动态内存模型
1.虚拟机栈也就是我们平常所称的栈内存,它为 java 方法服务,每个方法在执行的时候都会创建一个栈帧,用于存储局部变量表、运行时所需要是数据、操作数栈、动态链接和方法出口等信息。
2. 虚拟机栈是线程私有的,它的生命周期与线程相同。
//局部变量表
存放基本数据类型、引用类型、returnAddress类型。
***栈里面存放对象的引用,堆存放对象的实体
2、程序计数器
是一块较小的内存空间,是当前线程所执行的字节码指令的行号指示器,就是索引。
处于线程独占区。
如果正在执行的是Natvie方法,这个计数器值则为空(Undefined)
3、本地方法栈
虚拟机栈为虚拟机执行Java方法服务
本地方法栈和虚拟机栈类似,只不过本地方法栈为 Native 方法服务。
存放native方法的栈帧,与底层硬件方法接触
4、方法区(元空间)
1.7永久代是放在堆区的,1.8元空间是直接放在操作系统内存的
为什么有这种改变? 1、gc问题:堆区是用来存放对象的,又要存放类的元信息,而类的元信息有很少改变,就把它单独提出来放在直接内存中了。增大堆内存,减少gc次数。
1. 有时候也称为永久代,在该区内很少发生垃圾回收,但是并不代表不发生 GC,在这里进行的 GC 主要是对方法区里的常量池和对类型的卸载
2. 方法区主要用来存储已被虚拟机加载的类的信息、常量、静态变量和即时编译器编译后的代码等数据。
3. 方法区里有一个运行时常量池,用于存放静态编译产生的字面量和符号引用。该常量池具有动态性,也就是说常量并不一定是编译时确定,运行时生成的常量也会存在这个常量池中。
5、堆
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DEMpDOMQ-1682761932621)(E:\收藏\111\自己的学习\img\JVM堆内存模型.jpg)]
对象和类的关系-->一个类对应多个对象
存放对象的实例··
垃圾回收、性能调优都是在堆里
堆分新生代和老年代(分这些区域主要是为了垃圾回收)
新生代分为Eden、from、to (8:1:1 可用的空间是90%) (复制算法)
老年代 (新:老 1:2或者1:3 ) (标记-清除算法)
绝大部分对象放在新生代的Eden(伊甸园)区(因为詹姆斯.高斯林是基督教徒,基督教认为新生的人都是从伊甸园出生的),当Eden对象足够多达到临界值的时候,会触发GC(minor GC)。会进行可达性判断(DC roots)(判断这个对象有没有被别的地方引用,如果这个对象没有被别的地方引用,就会被抛弃。如果这个对象被别的地方引用着,那这个对象就会进入到from区,并且age+1). 然后继续创建对象,当再次触发Eden区的临界值,就会再次触发GC(minor GC),进行可达性判断,如果还在别的地方有引用,对象就会进入到from区,当from区满了之后内,会再次触发GC(minor GC),判断from区的对象是否处于游离状态,如果这个对象没有被其他地方用着,那么抛弃,释放内存。如果这个对象还被其他地方引用着,那么它会把这个对象的age+1(之前GC进入到from区的对象的age也+1,)并且拷贝到to区,此时from区变成to区,to区变成了from区。然后就这样一直循环,只要这个网站运行着,Eden区就会有源源不断的新对象,当minor GC一直被触发,触发到15次之后,有一些对象还没有被回收,那么它的age=15,JVM会认为这个对象是个老不死的对象,这个对象会进入到老年代。
当程序运行的时间越来越长,会有更多的对象进入到老年代,当达到临界值,再进入一个对象,老年代没有办法给它分配内存的时候,会触发(full GC,针对整个堆以及永久代的也会损耗一定的时间。所以我们在优化JVM的时候,减少Full GC的次数也是经常用到的办法。)就是STW(网站停顿),整个应用程序会被卡死,没有任何响应。当进入STW的时候,程序无法响应,垃圾回收也无法进行回收,
当老年代满了,会出现(OutOfMemoryError)表示堆内存溢出。
// `什么对象会进入老年代?
1、gc次数超过15次的,除了我理解的那个外还有 gc的年龄栈4个bit,就是2的4次方,1-15
2、空间担保(伊甸园区gc后,from区装不下,这个时候就放入老年代)
3、动态年龄判断(为了能更好地适应不同程度的内存状况,虚拟机并不是永远地要求对象的年龄必须达到了MaxTenuringThreshold才能晋升到老年代,年龄 1 到 N 的对象大小超过 Survivor 的 50% 时,则将大于等于年龄 N 的对象放入老年代。书里面的意思是,(同龄对象大小超过 50% 时,则将大于等于该年龄的对象放入老年代。因为如果是同龄对象的话,那么这批放入老年代的对象就必然是 1 岁,因为如果在 1 岁的时候没有达到 50%,那么在之后就更不可能达到 50% 了。)
4、大对象(对象大小超过Eden区一半,大小是动态计算的)
面试题
1、java为什么要做性能调优
在有限的物理内存上,做更多的事情,实现更好的性能。
绝大部分对象放在新生代
2、为什么需要采用分代回收的策略?
减少STW(网站停顿)的次数,其实就是减少full GC的次数,其实就是增大吞吐量。
5、内存结构(五大内存结构)内存模型(主内存、工作内存)
三、垃圾回收(GC)
GC
Minor GC
发生在新生代、比较频繁、执行速度快,触发条件:Eden空间不足,空间分配担保
Full GC
发生在老年代,较少发生,执行速度较慢。触发条件:调用 System.gc(),老年代空间不足,方法区空间不足
1、如何判定一个对象是垃圾对象
//引用计数法
在对象中添加一个引用计数器,当有地方引用这个对象的时候,引用计数器的值+1,当引用失效的时候,引用计数器的值就-1.当值为0的时候表示这个对象没用了,就是垃圾对象。但是JVM用的不是这种算法,因为两个垃圾对象,相互引用,这样垃圾对象的引用计数器的值并不为0,也不会被判定为垃圾对象。增加了时间和空间的消耗,就是存在这种缺陷问题。
//可达性分析法(目前用这个)
从GC roots这个根节点往下走,它所走过的路径称之为引用链,当一个对象对GC roots没有任何引用链,那么就判定这个对象是垃圾对象。
可以做为GC roots的对象:
虚拟机栈中引用的对象
方法区中静态属性引用的对象
方法区中常亮引用的对象
本地方法栈中引用的对象(Native方法)
何时回收
程序到达安全点或者安全区域的时候,就是所有的线程进入一个静止的安全点。
根据可达性算法,判断为垃圾对象后,并不是立即回收,而是经过一次标记,如果一个对象标记为有必要执行finalize()时,它会被放置在一个名为F-Queue的队列中,稍后由虚拟机进行垃圾回收。当在F-Queue时,虚拟机会对F-Queue中的对象作小规模的标记,如果发现此时某个对象又可达了,就会逃过GC的命运。
2、如何回收
//回收的算法:
1、标记-清除算法
两个过程,一个过程是标记,一个过程是清除,通过可达性分析法,分析这个对象有没有用,如果没用了,这个对象就会被标记,然后有一个专门的清除程序就把这些对象清除掉了。会有效率高,空间问题
特点:性能高,但是内存会出现越来越多的不连续的空间。
2、复制算法(解决标记-清除算法的不连续空间问题)
每次利用一半的空间,存放创建的对象,回收完之后,把存活对象移到另一半区域
特点:不会有空间碎片,但是浪费了一半内存空间
3、标记-整理算法
就是和标记-清除算法多了一个整理的过程,标记完了之后,把需要清除的移动到内存的一端,把不需要清除的移动到另一端,然后把需要清除的清除掉,解决了空间不连续问题。
4、分代收集算法(标记整理算法和复制算法的结合)
针对新生代和老年代不同的特点,采用不同的算法。新生代或者内存回收率高的,采用复制算法。对于老年代或者内存回收率低的,采用标记-整理算法。
//垃圾收集器
Serial(C 瑞 儿)新生代,采用复制算法
最基本、发展最悠久的垃圾收集器,它是一个单线程的垃圾收集器。适用于单CPU环境下的Clien模式,用于桌面应用。
Parnew(派 new)新生代 采用复制算法
是一个多线程的垃圾收集器,垃圾回收算法是复制算法。 parnew的关注点这垃圾回收的过程,降低时间,增加用户体验度。响应速度优先。
Parallel(派儿赖死) 新生代 复制算法
是多线程垃圾收集器,采用复制算法,是新生代收集器。parallel的关注点在吞吐量,吞吐量优先。(执行用户代码的时间/(执行用户代码的时间+垃圾回收的时间)),目的达到可控制的吞吐量。有两个重要参数:1、垃圾收集器最大停顿时间 2、吞吐量大小
Serial old
老年代,单线程,采用标记整理算法,客户端模式下默认的垃圾回收器
Parallel old
老年代、多线程、采用标记-整理算法,关注点是吞吐量优先
Cms
老年代,采用标记-清除算法。是一个多线程并发收集器。收集老年代。优点:并发收集,低停顿 缺点:占用大量CPU资源、无法处理浮动垃圾(就是我打扫过了,你再产生的垃圾我就不管了,等下次打扫再管)、会后空间碎片。需要结合Serial old做后备预案,来处理空间碎片。
工作过程:(初始标记->并发标记->重新标记->并发清理)
G1 内存块
新生代+老年代,最重要的垃圾收集器。JDK 9 以后的默认垃圾回收器
优势:1、多线程并行,能充分利用多核CPU的优势,缩短停顿的时间,提高速度、并发提高速度
2、分代收集,对每一个内存区域进行回收,不再区分新生代,老年代。
3、空间整合,采用的标记-整理算法,避免空间碎片
4、可预测的停顿
5、判断价值,值越高,停顿越少,回收后的空间可用率更高
垃圾收集器的搭配使用,。。。现在最主要的就是CMS G1
3、内存分配策略
所有的新生对象优先分配到Eden区
------分------割------线------
1、gc次数超过15次的,除了我理解的那个外还有 gc的年龄栈4个bit,就是2的4次方,1-15
2、空间担保(伊甸园区gc后,from区装不下,这个时候就放入老年代)
3、动态年龄判断(为了能更好地适应不同程度的内存状况,虚拟机并不是永远地要求对象的年龄必须达到了MaxTenuringThreshold才能晋升到老年代,年龄 1 到 N 的对象大小超过 Survivor 的 50% 时,则将大于等于年龄 N 的对象放入老年代。书里面的意思是,(同龄对象大小超过 50% 时,则将大于等于该年龄的对象放入老年代。因为如果是同龄对象的话,那么这批放入老年代的对象就必然是 1 岁,因为如果在 1 岁的时候没有达到 50%,那么在之后就更不可能达到 50% 了。)所以是我的那个理解。
4、大对象(对象大小超过Eden区一半,大小是动态计算的)
通过参数可以指定,这个默认值是根据内存区域的大小计算出来的。对于大对象我们认为它不是朝令夕改的,如果这个大的对象放到Eden区,那他需要不断地复制、移动,对于性能是非常不好的。
四、类加载机制
//概述:
JVM将指定的class文件读取到内存里,并运行该class文件里的Java程序的过程,就称之为类的加载;反之,将某个class文件的运行时数据从JVM中移除的过程,就称之为类的卸载。
把数据从class文件加载到内存,并对数据进行校验、解析、初始化,最终形成可以被虚拟机直接使用的Java类型。
//类加载时机:
反射、加载静态变量。
//类加载过程
1、加载:根据查找路径找到相应的 class文件然后导入
通过类的全限定名获取定义此类的二进制字节流,将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构,在内存中生成一个代表这个类的class对象,作为这个类的各种数据访问入口。
2、验证:检查加载的 class文件的正确性
验证是连接的第一步,确保class文件字节流中的信息符合当前虚拟机的要求,并且不会威胁虚拟机自身的安全。(文件格式校验、元数据校验、字节码校验、符号引用校验)
3、准备(连接):给类中的静态变量分配内存空间
为静态变量(加了static)和静态常量(加了 static final)分配内存并设置默认值(这个的初始值不是我们制定的值,而是数据类型的默认值,如果是常量或者被final修饰,指定的值会一同被指定)),变量使用的内存都将在方法区中进行分配。
4、解析:是虚拟机将常量池中的符号引用替换为直接引用的过程。
词法分析、语法分析、创建对应的语法树。
5、初始化:对静态变量和静态代码块执行初始化工作
执行类构造器,给类变量赋初始值,初始化静态代码块、构造方法以及初始化一些其他资源等。
//使用
//卸载
四+、双亲委派模型和类加载器
//什么叫类加载器
通过类的全限定名获取该类的二进制字节流的代码块叫做类加载器
主要有一下四种类加载器:
1、启动类加载器(Bootstrap ClassLoader)用来加载 java 核心类库,无法被 java 程序直接引用。
2、扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
3、系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH) 来加载 Java类。一般来说,Java应用的类都是由它来完成加载的。可以通过ClassLoader.getSystemClassLoader()来获取它。
4、用户自定义类加载器,通过继承 java.lang.ClassLoader 类的方式实现。
//双亲委派模型:类加载器会先让自己的父类来加载,父类无法加载的话,才会自己来加载。
JVM 启动的时候类的加载规则
原理:如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,如果直到最后一个子类还不能加载,就抛出ClassNotFound异常。这就是双亲委派模式
优势:避免了类的重复加载,
防止内存中出现多份同样的字节码,
防止核心API库被随意篡改
双亲委派机制可以打破,比如JDBC使用线程上下文加载器打破了双亲委派机制。Tomcat的Webapp目录
要自定义类加载器,就要继承ClassLoader
五、Java内存模型
JMM是java内存模型,规定java中所有的变量(指静态变量,常量等线程共享的变量,不包括局部变量等私有的变量)均存放于主内存中,每个线程都有各自的工作内存,工作内存中的变量是主内存的副本,线程只能对工作内存中的变量进行直接操作,并且线程之间是不能互相访问对方的工作内存的,只能依靠主内存进行通信。
六、强引用、软应用、弱引用、虚引用的区别?
//Java中的四种引用
强引用:强引用是我们使用最广泛的引用,类似于new出来的对象.如果一个对象具有强引用,那么垃圾回收期绝对不会回收它,当内存空间不足时,垃圾回收器宁愿抛出OutOfMemoryError,也不会回收具有强引用的对象;我们可以通过显示的将强引用对象置为null,让gc认为该对象不存在引用,从而来回收它;
软引用:软应用是用来描述一些有用但不是必须的对象,在java中用SoftReference来表示,当一个对象只有软应用时,(只有当内存不足时,才会回收它);软引用可以和引用队列联合使用,如果软引用所引用的对象被垃圾回收器所回收了,虚拟机会把这个软引用加入到与之对应的引用队列中;
弱引用:弱引用是用来描述一些可有可无的对象,在java中用WeakReference来表示,在垃圾回收时,一旦发现一个对象只具有软引用的时候,(无论当前内存空间是否充足,在下一次GC时会被回收);弱引用可以和引用队列联合使用,如果弱引用所引用的对象被垃圾回收了,虚拟机会将该对象的引用加入到与之关联的引用队列中;
虚引用:虚引用就是一种可有可无的引用,无法用来表示对象的生命周期,(任何时候都可能被回收),虚引用主要使用来跟踪对象被垃圾回收的活动,虚引用和软引用与弱引用的区别在于:虚引用必须和引用队列联合使用;在进行垃圾回收的时候,如果发现一个对象只有虚引用,那么就会将这个对象的引用加入到与之关联的引用队列中,程序可以通过发现一个引用队列中是否已经加入了虚引用,来了解被引用的对象是否需要被进行垃圾回收;
七、锁升级
在Java中,锁共有4种状态,级别从低到高依次为:无锁,偏向锁,轻量级锁和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级。
一个线程
偏向锁:偏向与
八、性能调优案例实战
方法区(元空间)的调优:
1、最小是20.75 ,最大是2的48次方(以64位机为例,不是2的64次!)
2、最大和最小一般设置为一样的,防止内存浮动。
3、大小=物理内存的1/32 (两个调试工具:arthas 命令行\VisualVM 图形界面)
4、预留空间20%-30% 就是如果你程序跑起来用了80m,那就设置110m
新生代与老年代的比列为:1:2或者1:3
eden与form与to的比例为:8:1:1
吞吐量优先的考虑:Parallel Parallel cld
响应速度优先的考虑:Parnew
首先知道了系统巅峰时期的并发,数据连接数,根据并发,算出来,一个订单生成的对象是一样,可以算出来生成多少的对象,可以设置栈内存、和堆内存。一般设置堆内存,生成的对象肯定是在伊甸园区,看他一秒生成多少对象,一秒之后,看她GC,改堆内存的最大内存、改数据库的连接。
虚拟机的参数:
-version查看版本及运行模式
-Xmixed使用混合模式,启动时使用解释器解释执行,程序运行后使用编译器编译热点代码为本地机器码,如果运行在server模式下,还会进行激进优化,如果激进优化失败,则退回到解释执行.
-Xint使用解释模式,全部解释执行.
-Xcomp使用编译模式,优先编译执行,无法编译时使用解释器解释执行.
-client使用client模式,不会进行激进优化.
-server使用server模式,会进行激进优化.
-XX+printGCDetails打印GC日志详细信息
-Xms堆的初始大小
-Xmx堆的最大大小
-Xss线程空间大小
-Xmn新生代大小,通常为-Xmx的三分之一或者四分之一
-XX:UseStringCache默认开启,缓存常用的字符串
-XX:PrintGCDateStamps打印GC的耗时
-XX:MetaspaceSize初始元空间大小和元空间GC阈值
-XX:MaxTenuringThreshold分代年龄的阈值
-XX:PretenureSizeThreshold大对象直接进入老年代阈值
-XX:NewRatio新生代与老年代的比例
-XX:SurvivorRatio新生代Eden和Survior的比值,默认为8.
-XX:PermSize永久代初始大小,1.8后自动忽略
-XX:MaxPermSize永久代最大大小,1.8后自动忽略.
-XX:+HeapDumpOnOutOfMemoryError让虚拟机在发生内存溢出时 Dump 出当前的内存堆转储快照,以便分析用
-XXHeapDumpPath=/home/admin/logs指定堆转储文件的路径.
-XX:+UseSerialGC使用指定收集器
-XX:+UseParallelGC
-XX:+UseParallelOldGC
-XX:+UseConcMarkSweepGC
-XX:+UseG1GC
JVM参数配置详解
-Xms JVM启动时申请的最小内存,默认为操作系统物理内存的1/64但小于1G
-Xmx JVM可申请的最大内存
-Xmn:2g 设置年轻代大小为2G。整个堆大小 = 年轻代大小 + 年老代大小 + 持久代大小 持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-Xss:128k 设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
-XX:NewRatio=4 设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
-XX:SurvivorRatio=4 设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6
-XX:MaxPermSize=16m 设置持久代大小为16m
-XX:MaxTenuringThreshold=0 设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论
///还可以配置垃圾回收器的选择
-XX:+UseParallelGC
-XX:ParallelGCThreads=20