JVM深入浅出(一)

JDK版本:1.8
官网:https://docs.oracle.com/javase/specs/jvms/se8/html/index.html

学JVM的终极目标:生产环境上的性能调优。

JVM的Server模式和Client模式的区别(了解)

后面讲的重点以Server模式为主。因为当Windows操作系统为32位时,JVM默认使用Client模式,如果是其它操作系统,当2G 2C以上默认是Server模式,低于这个配置是Client模式。一般64位都是Server模式。
在这里插入图片描述

运行模式

Java有几种模式:
解释型int:这种模式不会把Java代码转换成本地代码
编译型comp:这种模式第一次会编译成本地代码,第一次会慢一点
混合型mixed:代码是解释还是编译交由JVM来处理

JVM里的参数类型详解以及实操

1)标准参数 稳定的
什么叫标准参数,举个例子:java -version 、java -help等等,这些参数是不会变的。标准参数和这些类似。从jdk1.0到现在,这一批参数几乎不会发生变化。
2)X参数 相对变化较少的
可能会变,但是变得又不多,比如:
在这里插入图片描述
从上面可以看到加上-X有很多模式,解释模式去执行你的代码,编译模式去执行你的代码,混合模式去执行你的代码(这件事情是解释还是编译交由JVM来处理)。
那么Java是解释执行的还是编译执行的?不能确定,因为有个mixed混合执行。

再比如:
jdk7:有个东西叫永久代,在jdk8里面变成了Metaspace。发生了很大的变化。

3)XX参数 JVM调优的重点
XX参数分为:boolean布尔类型和非布尔类型。

举例:标准写法(boolean布尔类型)
-XX:[±]UseG1GC :表示把UseG1GC这个参数给启用或者禁用
还有一些参数是这样写(非布尔类):
-XX:MaxPermSize=64m :表示把MaxPermSize这个参数设置成64M

举个例子:
jps查看到某个pid(jps命令是查看Java进程用的),然后ps -ef查看这个pid的相关信息,可以看到很多相应的参数设置,如下:(jinfo -flags 直接跟pid可以列出所有的)
在这里插入图片描述

用PrintGCDetails这个参数再举个例子:
用IDEA开发一段代码:
在这里插入图片描述
按照下面方式可以看到,并没有启用PrintGCDetails这个参数。
在这里插入图片描述
怎么启用这个参数?
找到下面这个页面,并在VM Options里面设置:-XX:+PrintGCDetails
在这里插入图片描述
再启动代码,可以看到现在已经启动了这个参数:
在这里插入图片描述
再看IDEA控制台,已经把GC相关信息打印出来了:
在这里插入图片描述

再比如:如何查看某个进程使用的什么GC ?
可以这样看,先查到这个进程的进程号,然后可以这样:
jinfo -flag UseG1GC 进程号
jinfo -flag UseParallelGC 进程号
jinfo -flag UseConcMarkSweepGC 进程号

可以看一下这个进程使用了那种GC。下面用了UseParallelGC这种垃圾回收。
在这里插入图片描述
上面是布尔类型,下面是非布尔类型
比如:-XX:MetaspaceSize参数
在这里插入图片描述
可以看大MetaspaceSize是20多M,加入现在想把它设置大一点,设置成128M,如何设置?和上面类似。在这个地方加上-XX:MetaspaceSize=128m
在这里插入图片描述
启动代码,再看它已经变成了128M
在这里插入图片描述

jinfo命令详解以及实操

看帮助:
在这里插入图片描述

举例:
在这里插入图片描述
如下:

//这个是默认参数
Non-default VM flags: 
-XX:CICompilerCount=3 
-XX:InitialHeapSize=134217728 
-XX:MaxHeapSize=2124414976 
-XX:MaxNewSize=707788800 
-XX:MetaspaceSize=134217728 
-XX:MinHeapDeltaBytes=524288 
-XX:NewSize=44564480 
-XX:OldSize=89653248 
-XX:+PrintGCDetails 
-XX:+UseCompressedClassPointers 
-XX:+UseCompressedOops 
-XX:+UseFastUnorderedTimeStamps 
-XX:-UseLargePagesIndividualAllocation 
-XX:+UseParallelGC

//这个是IDEA中自己设置的参数
Command line:  -XX:+PrintGCDetails -XX:MetaspaceSize=128m 
-javaagent:D:\JetBrains\IntelliJ IDEA Community Edition 2018.2.8\lib\idea_rt.jar=54178:
D:\JetBrains\IntelliJ IDEA Community Edition 2018.2.8\bin -Dfile.encoding=UTF-8
PrintFlags系列参数及实操

-XX:+PrintFlagsInitial
-XX:+PrintFlagsFinal

在命令行输入:java -XX:+PrintFlagsInitial 回车后会有很多初始化参数。
在这里插入图片描述
= 是默认值
:= 是修改过的

-Xmx 、-Xms、-Xss参数及实操

-Xmx 、-Xms、-Xss这三个参数是属于XX参数类型里面的。
其中-Xms 就相当于 -XX:InitalHeapSize(JVM初始化堆大小)
-Xmx 就相当于 -XX:MaxHeapSize(JVM最大堆大小)
-Xss 就相当于 -XX:ThreadStackSize(JVM线程)

-XX:InitalHeapSize:这个JVM初始化堆大小占电脑的memory的1/64,如果电脑是12G内存,那么它差不多是200M。
-XX:MaxHeapSize:这个JVM最大堆大小占电脑的memory的1/4,如果电脑是12G内存,那么它是3G。

-XX:ThreadStackSize:和环境有关。

**生产上的最佳实践,一般设置InitalHeapSize等于MaxHeapSize。**最小的和最大的是相等的。

JVM运行时数据区
运行时数据区必看文档:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5

通过两个图来说明一下:
在这里插入图片描述
在这里插入图片描述
JVM运行时数据区中,有些区是JVM启动时就被创建,JVM退出时被销毁,跟随JVM,另外一些区是每个线程都有的,线程创建它就被创建,线程退出它就退出,跟随线程。

1.The pc Register:pc计数器。
不是重点。

2.Java Virtual Machine Stacks:Java虚拟机栈。
每个线程都有它自己的Java虚拟机栈,伴随着线程被创建。像抛出异常的时候,那些信息就是虚拟机栈里面打印出来的。

3.Heap:堆(重点)。
JVM的Heap(堆)是所有JVM线程所共享的。它是在JVM虚拟机启动的时候被创建。
Heap里面装的是:对象实列和数组(class instances and arrays)。
Heap里面存储的对象,由自动存储管理系统回收,这就是总所周知的GC垃圾回收,用垃圾回收器来进行管理。
举例:
你现在创建了一个class User。
然后在另一个地方new一个:User user = new User();
前面user是引用,后面new User()是对象,这个对象放在哪里?就放在Heap里面。

可以根据实现者的系统需求来选择合适的垃圾回收器。
堆可以是固定大小的,也可以根据计算需要进行扩展,如果不需要更大的堆,则可以收缩堆。堆的内存不需要是连续的。

如果计算需要的堆比自动存储管理系统提供的堆多,Java虚拟机将抛出OutOfMemory错误。

4.Method Area 方法区
JVM的方法区也是所有JVM线程所共享的。它并不是堆里面的。
方法区在jdk1.8里面就是那个Metaspace。

It stores per-class structures such as the run-time constant pool, field and method data, and the code for methods and constructors, including the special methods (§2.9) used in class and instance initialization and interface initialization.
方法区存储每个类结构,如运行时常量池、字段和方法数据,以及方法和构造函数的代码,包括在类和实例初始化以及接口初始化中使用的特殊方法。

User user = new User();
引用 对象
定义了一个User类,会有User.class文件,它就存储在方法区里面,就是Metaspace里面。
上面引用user是在栈里面,对象new User()是在堆里面。

方法区和堆是GC调优的重点
方法区和堆都能存东西,但是存的东西是不一样的。
方法区存的是class等,堆存的是对象。

5.Run-Time Constant Pool 运行时常量池
不是重点。

6.Native Method Stacks 本地方法栈

JVM的内存结构

在jdk1.8里面,有两大区域,堆和非堆。
JVM堆内存空间分为两个区域:新生代(Young generation)和老生代(Old generation)。新生代用以保存生存周期短的对象,而老生代则是保存生存周期长的对象。
新生代区域被进一步划分为三个子区域:Eden,Survivor0,Survivor1。
在这里插入图片描述
s0也叫from,s1也叫to。
so和s1大小都是一样的,在同一个时间点,只有一个是开启的,可用的,另外一个是空的,可以理解为主备。大部分情况下都是:User user = new User(); new一个对象首先放在Eden区,如果Eden区满了,就进行垃圾回收,如果对象还活着就放到s0区。每一次垃圾回收,会把Eden区和s0区的活的对象放到s1区,把其他的给清掉,这个时候Eden区和s0区就会空下来。每次回收都会有个年龄age的增加,每次加1。某个对象每次加1,当达到了某个限定值,就会被放到老年代区。前面是小GC,如果最终如果老生代也快满了,full GC(全局GC)就会启动。
Spark GC调优的目标就是确保老生代(Old generation )只保存生命周期长的RDD,而同时新生代(Young generation )的空间又能足够保存生命周期短的对象。这样就能在任务执行期间,避免启动full GC来收集任务执行期间创建的临时对象。

非堆区:
ccs:CompressedClassPointers压缩类指针
短指针:32位
长指针:64位
如果开启了ccs,就会使用短指针。
每new一个对象都会放在堆里面,每new一个对象有指向自己的class的指针,class是在方法区MetaSpace里面的。如果是在64位的环境下,指针是64位的,考虑到性能,有时候可以修改成32位的短指针。

CodeCache:跟JIT、jni有关系。如果编译了本地代码就在这里,如果没有编译就不在。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值