1.jvm 内存模型
pc寄存器:行号指示器,字节码解释器工作时就是通过改变这个计数器的值,来选下一条字节码指令,比如分支,循环,跳转,异常 处理,线程恢复等基础功能靠这个计数器来完成。ps:由于java的多线程是线程轮流切换的,一个处理器同一时刻只会执行一条线 程 (用来记录代码执行到哪里)
虚拟机栈:一个线程执行会有至少一个方法,没当线程进入一个方法,会在虚拟机栈创建一个栈帧,用于存储局部变量表(基本7种数据类型,对象引用)、操作帧,方法出 口等信息。每一个方法被调用即入栈,完成即出栈 ,
局部变量放在虚拟机栈
本地方法栈:
- 本地方法栈(Native Method Stacks)与 Java 虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的 Native 方法服务。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。
- Navtive 方法是 Java 通过 JNI 直接调用本地 C/C++ 库,可以认为是 Native 方法相当于 C/C++ 暴露给 Java 的一个接口,Java 通过调用这个接口从而调用到 C/C++ 方法。当线程调用 Java 方法时,虚拟机会创建一个栈帧并压入 Java 虚拟机栈。然而当它调用的是 native 方法时,虚拟机会保持 Java 虚拟机栈不变,也不会向 Java 虚拟机栈中压入新的栈帧,虚拟机只是简单地动态连接并直接调用指定的 native 方法。
方法区:存放编译代码类
静态变量
常量
堆:jdk 1.8 以后分为年轻代和老年代 ,jdk 1.8以前是永久代
1.字符串存在永久代中,容易出现性能问题和永久代内存溢出
2.永久代会为 GC 带来不必要的复杂度,并且回收效率偏低
3.永久代内存大小不太容易设置
new 出来的对象一开始存放在年轻代
年轻代:Egen so s1
2.对象在jvm 中的分布
3.jvm 垃圾回收过程
回收的是堆内存
JVM 参数配置 java -jar -Xmx10m -Xms10m -Xmn5m -XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=10M -XX:SurvivorRatio=8 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC
参数含义 Xmx: 分配的最大堆内存
Xms: 分配的最小堆内存
Xmn: 年轻代大小
XX:MetaspaceSize: 元数据空间大小 (1.7 memaryspacesize)
MaxMetaspaceSize: 最大元数据空间
SurvivorRatio: 年轻代与存活代大小配比
UseParNewGC: 年轻代垃圾回收算法
UseConcMarkSweepGC: 老年代垃圾回收算法
由于GC开始垃圾回收时会挂起应用线程,严重影响了性能,调优的目是为了尽量降低GC所导致的应用线程暂停时间、 减少Full GC次数
Xms、 -Xmx 通常设置为相同的值,避免运行时要不断扩展JVM内存,这个值决定了JVM heap所能使用的最大内存。
-Xmn 决定了新生代空间的大小,新生代Eden、S0、S1三个区域的比率可以通过-XX:SurvivorRatio来控制(假如值为 4 表示:Eden:S0:S1 = 4:3:3 )
-XX:MaxTenuringThreshold 控制对象在经过多少次minor GC之后进入老年代,此参数只有在Serial 串行GC时有效。
-XX:PermSize、-XX:MaxPermSize 用来控制方法区的大小,通常设置为相同的值。
1.避免新生代大小设置过小 ===========》频繁minor GC。 影响新能
2、避免新生代设置过大===============》执行回收时间长。
举个例子,过小: 家里买了个垃圾桶 ,太小, 经常容易满, 就要打, 很浪费时间哦;过大:发现垃圾变臭了, 扔垃圾的时候感觉很吃力, 这两种都需要避免;
1. 一般创建对象都是在各种方法里执行的,一旦方法运行完毕,方法局部变量引用的那些对象就会成为Eden区里的垃圾对象,可以被回收
2. 接着随着Eden区不断的创建对象,就会逐步的塞满,当然这个时候可能塞 满Eden区的对象里大多数都是垃圾对象。一旦Eden区塞满之后,就会触发一次 Young GC
3.Young GC 采用复制算法,把存活的对象放入S0,然后垃圾回收器直接回收掉 Eden区域里剩余的垃圾对象
下一次eden 满了之后,触发第二次Young GC so 的对象放在s1 ,s0和s1只会使用一种,便于垃圾回收的复杂度
4. 下一次如果Eden区满了,就会再次触发Young GC,把Eden区和S0区里的存活对象转移到S1区里去,然后直接清空掉Eden区和S0区中的垃圾对象
5. 系统运行一段时间之后,Eden区再次满了,这次发生了点意外,Eden存活的对象占用的空间大于S0的总空间,这时这部分对象就会进入老年代。
6.一旦老年代对象过多,就可能会触发Full GC,Full GC必然会带着Old GC,也就是针对老年代的GC,而且一般会跟着一次Young GC
对象进入老年代的情况:
1 一个对象在年轻代里躲过15次垃圾回收,年龄太大了,寿终正寝,进入老年代,可以使用参数控制 -XX:MaxTenuringThreshold 2 对象太大了,超过了一定的阈值,直接进入老年代,不走年轻代,可以使用参数控制 -XX:PretenureSizeThreshold
3 YoungGC过后存活对象太多了,导致Survivor区域放不下了,这批对象会进入老年代
4 动态年龄判断。可能几次Young GC过后,Surviovr区域中的对象占用了超过50%的内存,此时会判断如果年龄1+年龄2+年龄N的对象总和超过了Survivor区域的50%,此时年龄N以及之上的对象都进入老年代,这是动态年龄判定规则
Full Gc 的情况:
1 老年代自身可以设置一个阈值,使用参数-XX:CMSInitiatingOccupancyFraction控制,一旦老年代内存使用达到这个阈值,就会触发Full GC,一般建议调节大一些
2 在执行Young GC之前,如果判断发现老年代可用空间小于了历次Young GC后升入老年代的平均对象大小的话,那么就会在Young GC之前触发Full GC,先回收掉老年代一批对象,然后再执行Young GC。
3 如果Young GC过后的存活对象太多,Survivor区域放不下,就要放入老年代,要是此时老年代也放不下,就会触发Full GC,回收老年代一批对象,再把这些年轻代的存活对象放入老年代中
降低young gc 的频率 ,尽量避免full gc
查看gc情况:
1.查看java 进程 jps -l
2.jstat -gcutil pid 时间间隔 次数 dump 文件: jmap -dump:[live], format=b, file=filename