JVM-知识要点

228 阅读7分钟

HotSpotVm基本介绍

虚拟机

java堆(Java Heap)

堆内存划分:

  1. 新生代:新创建的对象或不是经常使用的对象。
    新生代中又分为eden区域,S0区(from区),S1区(to区)其中S0区=S1区。
  2. 老年代:存放比较活跃对象,经常被引用的对象。

垃圾回收机制在新生代中比较频繁,若多次gc没能回收,则放到老年代中,老年代gc次数相对比较少。
新生代和老生代默认划分为1:2

新创建对象会先放在eden区域,当发现经常被使用后会放到S0区,在S0区中gc回收时发现经常被使用,则放在老年代中,在老年代中,gc次数变少,则被回收机率变小。当S0中的对象发现不经常使用后,会被GC。
其中S0和S1有一个一定为空的,其目的是为了存放下一次的复制。
若在eden区中不可达,将会被直接GC。

如果对象在eden第一次发生MinorGC时仍然存活则进入Survivor区S0,S1。并开始设置年龄。如果在eden区中第一次发生MinorGC就不可达,便直接被回收。

垃圾回收机制判断对象存活

  1. 采用引用计数法--基本被淘汰(循环依赖问题)
    GC线程不定时进行回收时,如果对象被引用的话,会增加1,如果没有被继续回收会被减1.当为0时,则会被垃圾回收认为是不可达,会被清理。
  2. 根搜索(GC roots)
    类似于树形结构,使用是否与根节点有依赖关系,如果没有和GC roots有任何引用情况,则认为GC不可达。不可达则将进行垃圾回收。
    虚拟机栈,方法区引用对象属性,方法区常量,native等均可视为GC roots。

垃圾回收策略

  1. 标记清除算法:给对象做个标记。当对象在堆内存中被创建后,标记为存活。但标记清除算法会产生碎片化,当对象不再使用时则标识为不存活,此时垃圾回收机制会对标记为不存活的对象进行回收容易产生碎片化。gc次数高的时候,效率不高。一般应用在老年代。
  2. 复制算法:S0区与S1区之间的复制,可快速清除碎片。直接将经常使用的对象复制到另外一个区后,将该区清除。但由于每次有个区域是空的,所有相对比较浪费空间。一般在新生代中使用。
  3. 标记压缩算法:在标志清除算法的基础上,将可达和不可达对象进行排序。排序后,将不可对象进行回收。一般用到老年代中。
  4. 分代算法:新生代满了会触发Minor GC。老年代满了会触发Major GC/Full GC

新生代进入老年代条件

  1. 大对象直接进入老年代(通过设置-XX:PretenureSizeThreshold值,超过该值时跳过年轻代直接进入老年代)
  2. 长期存活的对象(超过阈值默认阈值15或设定阈值的对象)将进入老年代(通过设置-XX:MaxTenuringThreshold设置临界值)
  3. survivor中相同年龄所有对象的大小总和大于survivor区空间的一半,年龄比较大的或者等于该年龄的对象在MinorGC时将复制到老年代。
  4. 当MinorGC时存活对象过多,无法完全放倒Survivor区时,会借用老年代进行存放。(分配担保机制)

注意点:

  1. 其中新生代每次使用的内存空间不会超过90%
  2. 一次Minor GC时会将收集后Eden区和一块Survivor区清空
  3. Full GC会清空年轻代和老年代

垃圾回收机制

JVM不定时自动回收不可达对象(对象没有引用或没有其他地方需要继续使用,也可以采用将对象或引用对象设置为null)。
finalize可在垃圾回收之前进行执行。
System.gc();提示给gc可以进行回收垃圾,但实际并非会立即回收。

Java内存结构

class文件(字节码文件)--->类加载器
内存空间:

  1. 方法区:类的信息,常量,静态---永久区Full GC
  2. 堆内存:对象,数组。(垃圾回收机制算法,jvm参数调优,内存溢出和内存泄漏都是由于堆内存原因引起)
  3. Java栈:基本数据类型,局部变量(每个线程独立栈)
  4. 本地方法栈:Java与外部语言(例如:C,C++)之间的通讯。(例如:Android,应用层为Java 而底层为C,通过JNI,Java native interface调用C语言,提高执行效率)方法通过native(CAS)进行修饰。
    本地方法栈--->本地方法接口<----本地方法库
    本地接口调用执行引擎

JVM基本参数调优

JVM参数调优主要是对堆内存进行调优。
可采用Runtime.getRuntime()方法获取对应的内存信息。
配置新生代比例:eden需要配置比S0和S1大,一般为(eden)8:(S0)1:(S1)1
-XX:+PrintGCDetails打印GC日志
-XX:SurvivorRatio=2 eden区和S0,S1区的比例 2:1:1
尽量让新生代配置比较小一点,回收在新生代中进行回收,老年代中尽量不进行回收。
-XX:NewRatio=2 新生代与老年代的占比1:2 默认新生代与老年代的占比默认1:2

OOM异常

-XX:HeapDumpOnOutOfMemoryError OOM时导出heapDump文件
当内存快不足时会进行一次Full GC将新生代和老年代一起回收

栈溢出

递归调用时超出最大内存(循环的时候不会,没放到栈中)
当递归最大深度超过10636时约,将发生StackOverflowError栈溢出
通过-Xss5m 配置栈的大小来提高栈的大小

内存溢出和内存泄漏的区别

内存溢出:在申请内存空间的时候,超出最大申请的内存空间
内存泄漏:使用内存空间没有及时的释放,长时间占用内存,导致内存溢出。在使用IO,数据库连接,static等会一直占用内存最终导致内存溢出。

垃圾回收机制

并行回收:多线程回收,适合于吞吐量高的系统,回收时系统停止运行,时间相对比较短。
串行回收:单线程回收,执行垃圾回收时程序会停止,停止时间相对比较长。-XX:UseSerialGC使用

收集器

serial收集器:串行收集器,稳定且效率相对比较高,在jdk1.5时比较常用,但是属于串行回收方式。在新生代中采用了复制算法,老年代中采用了压缩算法。
ParNew收集器:Serial收集器的多线程版本。新生代并行,老年代串行。新生代采用复制算法,老年代采用标记压缩算法。-XX:+UseParNewGC可以采用-XX:ParallelGCThreads限制线程数
Paraller收集器:与ParNew收集器类似,更关注于系统的吞吐量。采用并行方式,用多线程通过扫描压缩堆,停顿时间段,回收效率高。
cms收集器:以最短时间为目前的收集器,采用标记-清除算法,响应时间有限,减少垃圾回收时间。采用多线程并行处理。-XX:+UseConcMarkSweepGC
g1收集器:采用G1算法进行回收,吸收了 CMS收集器的特定,支持大堆,高吞吐。支持多CUP和垃圾回收线程。在主线程暂停时适用并行,在主线程运行时采用并发收集。-XX:+UseG1GC

吞吐量

默认情况下每秒完成请求数。
垃圾回收机制只和初始值有关,不和最大堆内存无关。
采用jemeter监控

JDK可视化工具

对当前系统的分析

java常用的命令

jconsole

在java bin目录下的jconsole
可监控java程序
监控一段时间内的堆内存使用情况,线程使用情况,类使用情况,CPU占用等。

JavavisualVM

在java bin目录下

字节码

可以使用字节码技术对类的基本信息进行操作,新增属性或方法,修改属性或方法,新增类等。例如:lombok,AOP,动态修改class文件等。
动态代理:采用CGLIB框架,实现动态代理效果。

字节码常见操作库

BCEL
ASM
CGLB
Javassist:比反射开销小,能能高。但是不支持内部类,匿名类,continue,break,泛型,枚举等。

javassist

在pom中引入javassist
代码中引入ClassPool类似反射
可以动态创建类,编译后的class。

类加载器

java-》编译class文件--》虚拟机指令
加载-》连接(验证,准备,解析)-》初始化

热部署

热部署案件:SpringBoot,tomcat,jetty等
JVM只会有一次classLoad