JVM笔记(一)

JVM笔记

基础知识

  1. 使用javac,编译**.java**文件为字节码文件,字节码文件是16进制的文件

    javac UserItemController.java
    

    Tip: 可以加入-d参数,来指定生成字节码的位置,不指定则默认当前文件路径

  2. 使用javap,将**.class**文件通过反编译,获得16进制对应的jvm指令

    javap -verbose -c UserItemController.class
    

    Tip: 加入-verbose参数可以展示出更多的信息,比如比较关键的类的常量池,也叫class 文件常量池(constant pool,也简称cp)。JVM运行期间的运行时常量池就是由一个个类的class 常量池组成的。在类里的各个方法的调用,也都是使用jvm指令+常量池编号的方式进行调用

  3. 当一个JVM线程被创建时,每个线程都有属于它自己的PC计数器,同时会创建一个属于这个线程的栈,栈的定义可以参考官方文档,栈里面有存储着帧,当一次线程调用过程中需要调用N个方法,那么调用每一个方法时,都会创建一个帧,当该方法正常调用结束,或者出现异常,则帧被销毁。

    • 帧的官方定义

    • 帧包含局部变量数组(也叫局部变量表)、操作数栈,这两个东西的大小在编译期间就确定了
      在这里插入图片描述

      • 局部变量表中存储着当前帧(也就是当前调用的这个方法)的参数,如果该方法是一个构造方法或者是实例方法(就是通过一个类的对象调用这种方式,A a = new A(); a.get()😉,那么index0存的是当前类的引用this.为什么第一位置必须存当前类的this引用,因为访问当前类的实例方法时使用this.
        在这里插入图片描述
  4. 运行时数据区域(官方称为为run-time data areas)包括以下

    • 程序计数器-pc Register
    • java虚拟机栈-jvm Stacks
    • 堆-Heap。所有线程共享
    • 方法区-Method Area。所有线程共享
    • 运行时常量池-Run-Time Constant Pool

    Tip: 官方文档在2.5小节中将以上部分都视为同一级别来介绍,但是在文档中又做了一些说明,如:方法区逻辑上是堆的一部分,方法区中又包含了运行时常量池在这里插入图片描述

    注意:上面所说的堆-Heap,目前基本都称作JVM上逻辑的堆,因为Java官方上的Heap并没有明确定义逻辑上的堆。只是一般的不同厂家的JVM实现,将逻辑上的堆又分为了:

    堆-GC Heap和非堆-Non-Heap。划分的依据是,程序中对象的创建、回收对象基本都在GC heap上

    至于Non-Heap 里放置是一般都不会改变的东西,metaspace下放置的就是永久代(JDK8已经没有了永久代的说法),还有上面说的方法区、运行池常量池都被移动到了metaspace中

常用启动参数

官方标准参数,参数的设置无非是两种。
在这里插入图片描述

boolean类型和=类型,boolean类型参数使用+/-来进行开启/关闭

如:-XX:+HeapDumpOutOfMemoryError,开启堆内存溢出自动转储堆快照

=类型则是设值用的。

如:-XX:initialHeapSize=4g,设置堆初始大小为4g

  1. 设置初始堆内存,如果后面跟数字没有任何单位,则默认为byte字节,单位可以为k/K,m/M,g/G

    -XX:InitialHeapSize=4g
    
  2. 设置最大堆内存,如果后面跟数字没有任何单位,则默认为byte字节,单位可以为k/K,m/M,g/G

    -XX:MaxHeapSize=4g
    
  3. 设置创建线程时,线程栈的大小。上面基础知识说过,创建线程的时候会同时为该线程创建一个线程栈.在64位的机器上,默认每个线程栈是1m

    -XX:ThreadStackSize=1m
    
  4. 设置年轻代初始堆内存,官方建议设置为整个堆内存的一半、或者四分之一。年轻代内存太小会导致大量的minor GC.如果太大,当full GC时,清理内存的时候会比较浪费时间

    -XX:NewSize=2g
    
  5. 设置年轻代最大堆内存

    -XX:MaxNewSize=2g
    
  6. 设置年轻代中Eden区域和每一个Survivor区域(from、to)所占的比例.默认值是8

    -XX:SurvivorRatio=4
    
  7. 开启 打印GC日志细节、GC发生时间。一般和非官方标准的第四条一起使用

    -XX:+PrintGCDetails
    -XX:+PrintGCDateStamps
    # 一般设置了下面的第五条,会默认开启下面的设置
    -XX:+PrintGCTimeStamps
    
  8. 开启当堆内存溢出时自动dump堆快照

    -XX:+HeapDumpOutOfMemoryError
    
  9. 保存堆快照到文件中

    -XX:HeapDumpPath=/home/java_pid%p.hprof
    
  10. 分代回收时,设置年轻代的的提升阈值,对象经历过15次young GC之后,迁移到老年代

# 如果设置为0,则GC之后存活的对象直接迁移到老年代
-XX:MaxTenuringThreshold=15
  1. 开启串行GC回收算法

    -XX:+UseSerialGC
    
  2. 开启并行GC回收算法

    -XX:+UseParallelGC
    
  3. 开启CMS回收算法

    -XX:+UseConcMarkSweepGC
    
  4. 开启G1回收算法,G1是Garbage-First的简称

    -XX:+UseG1GC
    

非官方标准参数,但是等同于官方参数的缩写

  1. 设置初始堆内存大小,-xms后面直接跟数字+单位,如果后面跟数字没有任何单位,则默认为byte字节,单位可以为k/K,m/M,g/G

    -Xms4g
    
  2. 设置最大堆内存,-xmx后面直接跟数字+单位,如果后面跟数字没有任何单位,则默认为byte字节,单位可以为k/K,m/M,g/G

    -Xmx4g
    
  3. 设置线程的线程栈大小,-xss后面直接跟数字+单位,64为的机器默认为1m

    -Xss1m
    
  4. 保存GC日志到文件中,一般和官方标准的第四条一起使用。

    -Xloggc:/var/log/gclog/gc.log
    
  5. 设置年轻代的初始堆内存和最大堆内存大小

    # 相当于同时设置了 -XX:NewSize=2g -XX:MaxNewSize=2g
    -Xmn2g
    

以上所有参数,以及参数说明,均可在附录中的JVM参数链接中找到

参数简单测试

问:JVM的参数为 -Xms2g -Xmx4g -Xss512k -Xmn1g -XX:SurvivorRatio=3,将上述非标准参数,转为JVM标准参数表示.并解释什么含义

答: -XX:InitialHeapSize=2g -XX:MaxHeapSize=4g -XX:ThreadStackSize=512k --XX:NewSize=1g -XX:MaxNewSize=1g -XX:SurvivorRatio=4.设置初始堆内存为2g,最大堆内存为4g,线程栈大小为512k,年轻代初始所占的堆内存为1g,年轻代最大占的堆内存为1g,年轻代中Eden和Survivor比例为3:1

问:对于上述参数,年轻代中Eden和Survivor个占用具体多少内存

答:年轻代最大所占的堆内存为1g,Eden和Survivor所占比为3:1,即一个Eden/一个Survivor=3.年轻代中有两个Survivor。则1g/(3+1+1)=200m,则Eden = 200 x 3 =600, Survivor = 200 x 2 = 400

对于最大堆内存(-xmx/-XX:MaxHeapSiz=)配置多少合适?

去掉系统或者容器自身用的内存,剩余的可用内存的70%-80%,如果系统中还有其他应用使用内存,则还要降低点。注意,因为Java进程本身的内存占用是不计算在xmx范围内的,所以计算剩余的可用内存的时候,要注意Java进程和其他进程占用的系统内存大概使用了是多少。反例:服务器物理内存8G,出去系统本身可能还有7.5G,如果没有特别多的其他进程,则可以使用7.5G*0.8=6G。如果其他进程很多,就要降低6g这个值了,防止影响其他进程

初始堆内存(-xms/-XX:InitialHeapSize=)和最大堆内存(-xmx/-XX:MaxHeapSize=)要不要配置成一致的?

一般建议配置成一样的。防止初始内存不够,扩容时带来的内存抖动(最后一句话,待查阅资料佐证)

工具

查看字节码工具

在IDEA下安装jclasslib插件,可以很方便的看到当前类的字节码结构,以及很方便的看到各个方法的中具体执行的JVM指令。点击具体的JVM指令,会自动跳转到官方JVM指令的文档,很方便的看到当前指令的作用是干嘛的

附录说明

  1. 所有Java命令都可以使用 命令 -help 的形式,来查看帮助。Mac下的提示都是中文的,很友好

    例如:

    # 查看javap反编译工具的使用
    javap -help
    
    # 查看javac编译工具的使用
    javac -help
    
  2. JDK8的JVM规范

  3. 基本类型对应的JVM操作指令

  4. JVM指令集以及说明

  5. IBM关于字节码的文章,写的不错

  6. jrebel上的文章,为什么要知道Java字节码,写的不错,还可以在jreble官网的Blog板块搜索java byte code,查看更多有关的博文

  7. JVM参数清单

  8. JVM结构

  9. JVM结构2

  10. 安装JDK的好用地址

疑问

  1. 当web请求进入Tomcat之后,具体到调用controller的之后,对于JVM来说,进入Tomcat的那一个开始是不是就算创建了一个JVM线程?对于后面的Tomcat->controller->service->mapper都只不过是该线程上的某一个栈帧?

什么是内存泄漏?

Java 中的内存泄漏,就是那些逻辑上不再使用的对象,却没有被 垃圾收集程序给干掉。从而导致垃圾对象继续占用堆内存中,逐渐堆积,最后产生 java.lang.OutOfMemoryError: Java heap space 错误

而内存溢出,则是内存不够用,导致无法分配足够的内存空间,而发生的错误。

其他

Java程序中生成线程,实际会请求操作系统创建一个操作系统级别的线程。所以,这些线程并不会占用Java中的堆内存,而是在操作系统层面分配内存。
在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值