jvm
jvm运行和类加载全过程
先加载了类,加载完在进行初始化 初始化完成后加载该方法
jvm 先进行静态代码块 构造代码块 然后构造器 (多个静态资源取决于编译的先后顺序)
类成员在对象创造前执行 static修饰的变量 在创建对象前进行初始化操作 储存在堆中
运行时常量池在元空间(方法区就是元空间) 字符串常量池在堆
jvm内存结构
栈 stack:
- 每个线程私有,不能实现线程间的共享!
- 局部变量放置于栈中。
- 栈是由系统自动分配,速度快!栈是一个连续的内存空间!
堆 heap:
- 放置new出来的对象!线程共享
- 堆是一个不连续的内存空间,分配灵活,速度慢!
方法区(也是堆):
- 被所有线程共享!
- 用来存放程序中永远是不变或唯一的内容。(类代码信息、静态变量、字符串常量)
主动引用和被动引用
深入类加载器
启动类加载器–》扩展类记载器–》应用程序类加载器–》自定义类加载器
不是继承 由组合关系实现
jvm虚拟机
class 模板反射对象 全局唯一
.java文件经过Javac(java编译器编译称为.class字节码文件),然后再jre(保证代码的安全)中经过类加载器 字节码校验器 解释器 然后运行到系统平台
getclass 返回此class运行时类
getclassloader 获取类加载器
栈里边存堆的地址指针 栈是引用地址
双亲委派机制
虚拟机自带的加载器
启动类加载器
扩展类加载器
应用程序加载器
APP-->exc--boot
1 类加载器收到类加载的请求 加载初始化
2 将这个请求向上委托给父类加载器去完成 直到启动类加载器
3 启动加载器检查是否能加载当前类 能就使用当前加载器加载 不能就抛出异常 通知子加载器进行加载
4 重复步骤3
找不到就报classnotfound
沙箱安全机制
字节码校验器
常量池
常量池表:Class文件中存储所有常量(包括字符串)的table。这是class文件的字节码指令
运行时常量池:JVM内存方法区的一部分,这是运行时的内容
字符串常量池:也在方法区中,jvm实例全局共享 全局只有一个
native:
凡是带了native关键字的 Java范围达不到了 回去调用底层的c语言库
会进入本地方法栈 调用本地方法 本地接口 JNI
JNI:扩展java的使用 融合不同的编程语言为java所用 调用c c++的程序
他在内存中专门开辟了一块标记区域 Native Method Stack
在最终执行的时候 通过java接口 加载本地方法库中的方法(java驱动打印机)
pc寄存器
每一个线程都有一个程序计数器 线程私有 是一个指针 指向方法区的方法字节码
方法区
所有线程共享 构造函数 接口代码在这定义 所有定义的方法信息都保存在该区域
静态变量,常量,类信息(构造方法和接口定义)运行时常量池存在在方法区,但实例变量存在在堆内存中,与方法区无关
程序计数器
程序计数器是一个比较小的内存区域,用于指示当前线程所执行的字节码执行到了第几行,是线程隔离的
虚拟机栈
虚拟机栈描述的是Java方法执行的内存模型,用于存储局部变量,操作数栈,动态链接,方法出口等信息,是线程隔离的
原则上讲,所有的对象都在堆区上分配内存,是线程之间共享的
小结
GC
堆
heap 一个jvm只有一个内存 堆的大小是可以调节的
内存满了 oom 堆内存不够 java.lang.OutOfMrmoryError:java heap space
类加载器读取的文件后 一般会把什么东西放在堆中 类 方法 常量 变量
堆分为三个区:新生区(伊甸园区 幸存0区-这两个区可能产生对象的互换-幸存1区) 养老区 永久区改为元空间(1.8移除了永久区的概念)
GC主要在堆中(伊甸园区和fromto区)栈区的内存随着线程释放而释放
新生区:类诞生成长甚至死亡的地方 还持有引用就能在伊甸园区第一次轻GC中活下来 新生区整体满了以后触发 一次重GC ,活下来的进入养老区
永久区:这个区域元素常驻,存放jdk自带的class对象 interface元数据 存储的是Java运行时的一些环境或者类信息 不存在GC 关闭虚拟机会释放这个区域的内存 OOM(Out Of Memory)
当一个启动类加载了大量的jar包 大量动态生成的反射类 tomcat部署的太多的应用 永久区可能会崩
永久代1.6 永久代 常量池在方法区
永久区1.7 永久代 慢慢退化 去永久代 常量池在堆中
元空间jdk1.8 无永久代 常量池在元空间
元空间:逻辑上存在 物理上不存在
OOM
内存快照分析工具 MAT Jprofiler
例子:Xmx10240m -Xms10240m -Xmn5120m -XXSurvivorRatio=3
-Xmx:最大堆大小 默认物理内存的1/4 -XX:MaxHeapSize
-Xms:初始堆大小 默认物理内存的1/64 -XX:InitialHeapSize
-Xmn: 年轻代大小
-XX:+PrintGCDetails //打印GC垃圾回收
-Xss: 单个线程栈的大小 默认 512K~1024K -XX:ThreadStackSize
-XXSurvivorRatio:年轻代中Eden区与Survivor区的大小比值
-XX:MaxTenuringThreshold=15 通过这个参数可以设定进入老年代的时间
年轻代5120m, Eden:Survivor=3,Survivor区大小=1024m(Survivor区有两个,即将年轻代分为5份),Survivor总大小为2048m。伊甸园区:from:to=3:1:1。
-Xms初始堆大小即最小内存值为10240m
GC垃圾回收机制
jvm在进行GC的时候,并不是对这三个区域统一回收,大部分时候都是回收的新生代
- 新生代
- 幸存区 from to 幸存两个区是一个交替的过程 谁空谁是to
- 老年区 当new出来的对象过于大时,该对象会直接进入老年代
GC的两种类:轻GC(minor gc) 重GC(full gc)
STW Stop the world:做gc的时候系统会停止用户线程(电脑会卡一下)
full gc触发条件:
程序执行了System.gc(); 不一定会执行,提醒JVM执行gc
执行了Jmap -histo:live pid命令 ///立刻触发full gc
在执行轻gc之后进行一系列检查
轻gc和重gc的执行条件
JVM检查老年代最大连续空间>新生代所有对象的总大小,执行轻gc,反之则
JVM检查是否开启空间分配担保机制,没有开启执行重gc,反之则
老年代最大连续空间<历次晋升到老年代中的平均大小,执行重gc,反之则执行轻gc
轻gc失败执行重gc
finalize对对象操作 只执行一次 GC的时候执行完对象被销毁
A: 垃圾回收在jvm中优先级相当相当低。
B:垃圾收集器(GC)程序开发者只能推荐JVM进行回收,但何时回收,回收哪些,程序员不能控制。
C:垃圾回收机制只是回收不再使用的JVM内存,如果程序有严重BUG,照样内存溢出。
D:进入DEAD的线程,它还可以恢复,GC不会回收
GC算法
-
引用计数法 给每一个对象加一个引用,使用过就加一 最后为0的就被清理(有点像索引 乐观者模式)
-
复制算法 当两个幸存区都有东西的时候,因为谁空谁是to,需要通过复制把他们放在一个幸存区(一直有一个空的幸存区,复制算法主要在新生区(Eden from to)(好处:没有碎片。坏处:浪费空间。)
最佳使用场景:对象存货率较低。 -
标记清除算法:扫描对象 堆活着的对象进行标记 对没有标记的对象清除
优点:不需要额外的空间 缺点:两次扫描 严重浪费时间 会产生内存碎片
-
标记压缩(整理):防止内存碎片的产生 存活的对象像一段移动 多了一个移动成本
标记清除压缩
总结
GC:分代收集算法 年轻代用复制算法 存活率低
老年代用标记压缩清除算法 区域大存活率高 调优:内存碎片差多少
jmm
1 什么是jmm
Java Memory model
2 他干嘛的
作用:缓存一致性协议,用于定义数据读写的规则
jmm定义了线程和主内存之间的抽象关系:线程之间共享变量储存在主内存Main Memory中,每个线程都有一个私有的本地内存(local Memory)
volatile:保证可见性 解决指令重排
上图store和white写反了反射机制