Jvm 的工作相当于翻译 将java.class 转化成各个平台能识别的机器码
JVM运行过程
运行数据区也就是jvm所管理的内存区域 里面包含程序计数器 虚拟机栈 本地方法栈 方法区 以及堆
方法区:.class 类信息 静态变量 常量
Jdk 1.7 永久代
Jdk1.8 元空间 好处:方便拓展(使用机器内存) 坏处:机器内存总共20g 元空间15g,那么堆空间 只能5g的了 ,压缩堆空间
堆: 对象存放的地方 (几乎所有) 数组
方法区以及堆 两个地方存东西 为什么步合并成一个地方存数据?
因为堆里面的信息 频繁回收创建
方法区 里面的信息 基本不会被回收掉
程序计数器 :相当于指针的含义 指向正在运行字节码行数
为什么需要程序计数器 ?
因为时间片轮转机制 被挂起之后 在次获得cpu的时候知道上次执行到哪里
作为jvm内存区域唯一不会oom的内存区域 程序计数器 以为程序计数器只需要个Int类型的内存空间就可以的
虚拟机栈 (java栈) 先进后出 后进先出
栈帧
局部变量表 八大基础数据类型
操作数栈 执行每一行代码的操作
动态链接
完成出口 main 方法中 执行work方法执行完之后 在main的栈帧中存放work返回的数据
知识点补充:
八大基本类型 字符类型 char 布尔类型 boolean
数值类型 整数类型 int short long byte
浮点类型 float double
基本类型和包装类的区别
Int 是个基本的数据类型 Interge 是一个类 使用的时候 要进行初始化 int就不用初始化
Jvm是 栈操作 这里的在栈指的栈帧里面的操作数栈 不是指的虚拟机栈 优缺点: 兼容性好 效率低 因为要频繁的进栈出栈操作
Android 或是 c 是 寄存器操作 操作步骤少 相当于把栈中的局部变量表和操作数据融合在一次 减少操作步骤 优缺点 : 操作速度快 可移植性差
本地方法栈:
本地方法栈 保存的是native本地方法 c++执行的操作
从数据共享以及私有的角度考虑
- 程序计数器 虚拟机栈 本地方法栈 属于线程私有的数据区(线程隔离数据区)这个相当于个套餐 启动个线程 每个线程就会有 三个数据区
- 方法区和堆 是共享数据区
查看当前多有的java进程 cmd里面 打jps
内存溢出:
- 栈溢出 stackoverflow 栈内存溢出 还有一种情况是 一个线程起一个虚拟机栈(默认占1m) 如果机器只有500m 启动1000个线程 的话 那么也会包oom 栈溢出
- 堆溢出 超过堆的内存
- 方法区内存溢出
- 本机直接溢出
虚拟机优化技术
方法内联 实现同样的功能 尽量代码简洁
栈帧之间数据共享
虚拟机中对象创建过程
检查加载:检查这个类有没有
分配内存 :用下面的那个方法取决于 垃圾回收器方法 带不带整理功能
指针碰撞: 堆空间比较规整时候
空闲列表: 垃圾回收之后 内存空间不规整
并发的问题 :CAS 浪费性能 本地线程分配缓冲 TLAB 占E等区的1%
内存空间初始化:对象的参数进行 零值操作
设置 :设置对象属于那个类 对象头
对象的 是由对象头+实例数据 +对齐填充 凑够8字节的整数倍
如何判断对象是否存货
- 引用计数算法
- 可达性分析法
Object 里面的Finalize 可以进行对象自救 只能执行一次 线程级别很低,可控性不高
Java中各种引用
软引用:当内存空间要发生oom的时候 对象就会被回收 使用场景:图片缓存 或是网页缓存
弱引用:发生了gc 不管内存够用不够用 直接回收了
虚引用:监控垃圾回收器 是否正常工作
软引用和弱引用对比 建议还是用弱引用 因为软引用是要发生oom的时候 才回收 假如说 还剩100m空间 软引用20m 强引用100m 哪怕是软引用回收了 也会引起oom的,弱引用基本不会出现这个问题 因为要发生oom 之前一定进行了多次的gc, 这样的话,多次回收之后 空间就有可能够用了
Oom异常的时候 catch里面不用Exezipation 而是要用 throwable (可抛出 异常)
逃逸分析 指的是 对象作用域 只在栈帧里面应用 不被外面所调用 。如被外面调用的话 方法逃逸 能被外部线程赋值 线程逃逸 就是逃逸成功 就不能栈上分配
- 绝大多数的对象都是朝生夕死 98%死 新生代 eden from to 8:1:1
- 对象熬过多次垃圾回收,越来越难回收 老年代 Tenured
Jvm中 新生代和老年代所占空间比例 1/3 2/3 也就是 1:2
复制算法适用在新生代
为什么增加eden区 增加空间利用率 正常的复制算法 一半存放对象,一般做预留操作,这样的利用率只有50%的 增加eden区的话 复制算法只在from和to区进行执行eden from to 的比例为8:1:1 那么利用率就是90%
这个算法适合老年代 不适合新生代 因为新生代98%的对象都要回收 标记就要标记90% 标记多了也费性能,得不偿失 老年代基本是难回收的对象 所以适合
Serial 串行 单线程
Parallel 并行 多线程并行
Concurrent 并发 cms中的c 多线程并发
Serialold 可以和 新生代 serial parnew parallel 回收器搭配使用
Cms 可以和新生代 serial parnew 以及老年代的 serialold 搭配使用
Parallelold 只能和新生代的parallel 一起使用
并行 :启用多个线程 多个垃圾回收器一起工作 但是用户线程要全部停止
并发: 启用多个线程 多个垃圾回收器线程可以和用户线程一起工作
Serial 单线程垃圾回收器 和并行paralell多线程垃圾回收器都要 暂停所有用户线程 stop the word 这样会影响用户体验
CMS concurretn 并发
标记清除算法 两个步骤 第一步是 标记 第二部是清除
Cms 就是在这两个地方进行优化
拆分标记 初始标记: 只标记根可达的对象 根可达对象少 花费时间少
并发标记: 开启线程 对上面根可达对象的下面的对象 进行标记操作,这块耗时
重新标记 :因为标记线程和用户线程一起运行所以会有遗漏的没有标记,所以要重新标记
清除操作:并发清除-----》重置线程 --》用户线程
Cms的优点: 减少暂停用户线程的时间,提高用户体验感 不卡顿
缺点: 1.cpu敏感 占用一个cpu做垃圾回收,用户线程减少,增加用户线程负担
2.浮动垃圾 在并行清理时候出现的垃圾只能下次在进行清除 浮动垃圾过多的时候 马上要oom的时候 会使用serialold垃圾回收器
3.内存碎片 因为cms采用的是标记清除的算法所以会产生内存碎片
G1的理解 :不在划分老年代和新生代 将内存等分 eden survivor old humongous(大对象 (定义:超过平分的内存就是大对象))
处理方式和cms类似 初始标记 并发标记 重新标记