【JVM】深入浅出

Java运行时数据区(内存)
1.线程私有
程序计算器:指向当前线程正在运行的地址。为什么java 是多线程的,多线从意味着存在切换 确保多线程情况程序正确执行

栈:数据结构
本地方法栈:保存native方法信息,当调用native方法是虚拟机栈不会保存栈帧,JVM只是简单动态链接直接调用native方法

虚拟机栈(-Xss 1M):存储当前线程运行方法所需要的数据/指令/地址
每个方法在执行时都会创建一个栈帧,栈帧包括 局部变量表/操作数栈/动态链接/返回地址

为什么要使用栈。
更好的实现方法调用方法

2.线程共享
方法区((JDK1.7前)永久代/(JDK1.7后)元空间):类信息/final常量/static静态变量/即时编译后的代码

永久代和元空间的区别,分配内存的方式 以及垃圾回收机制。永久代之前和堆的垃圾回收差不多

Java堆:

3.运行时常量池 1.7 在堆中  1.6在方法区
符号引用
字面量

4.JVM 碰到new指令
检查/加载->分配内存空间->内存空间初始化->设置->对象初始化
分配内存空间:指针碰撞/空闲列表。多线程指向同一内存 使用CAS确保原子性
内存空间初始化:比如碰到 int类型赋值0,如何类型赋予默认0值
设置:元数据 hash值 垃圾回收 年龄 类地址等


5.对象的内存布局
对象头(8个字节的整数):类型指针/hash值/垃圾回收/线程持有的锁/锁偏向等
对象数据:代码定义的各种类型
对齐填充:对象的大小必须是8个字节,实现数据7个字节,填充1个。

6.对象的访问方式
使用句柄:通过句柄池找到具体的对象
直接指针:直接指向对象

7.堆内存模型
新生代:新生代大小参数-Xmn20m表示新生代大小为20m(初始和最大)-XX:SurvivorRatio=8 默认8:1:1
Eden:
from:
to:

老年代:


8.堆内存分配策略
所有对象优先分配Eden区
大对象存放老年代
长期存活对象将进入老年代
动态对象年龄判断
空间分配担保


参数设置:
-Xms 初始化堆大小
-Xmx 堆最大值
-Xmn 初始化新生代大小
-XX:PrintGCDetails 打印GC详情
-XX:PretenureSizeThreshold=4m 对象超过4M进入老年代

9.GC如何判断对象的存活
1.引用计数   缺点:循环引用问题
2.可达性分析
GC Roots 包括:
方法区静态属性引用的对象
方法区常量引用的对象
虚拟机栈(本地变量表) 引用的对象
本地方法栈JNI(native)引用的对象

每个Object 都会有finalize ,实现这个方法没有GC Roots 引用 可以拯救对象,但是JVM不一定会条用。  坑。。。

10.引用
强引用  内存溢出不会回收,直接保存
软引用  内存溢出,GC会回收
弱引用  每次GC,都会回收
虚引用  场景 TreadLocal ,weakHashMap

11.GC
Minor GC
Full GC

复制算法
简单高效,没内存碎片
内存利用低,只有一半
存货对象多时效率低

新生代使用(From To)

标记-清除算法
利用率百分百
标记和清除效率不高
内存碎片

标记-整理算法
利用率百分百
没内存碎片
效率不高
相对标记-清除低

JVM一般都是分代收集
收集器
新生代:
Serial 复制算法  单线程
ParNew 复制算法  并行多线程
ParallelScavenge  复制算法  并行多线程
老年代:
Serial Old  标记整理算法  单线程
Parallel Old 标记整理算法 并行多线程
CMS 标记清除算法 并行与并发收集
G1 跨新生代和来年代 复制算法/标记整理+化整为零 并行与并发收集器

CMS 标记-清除算法
步骤
初始标记  - 暂停所有用户线程 标记那些是GC ROOT
并发标记  -同时进行 可达性分析
重新标记  -暂停
并发清理  -同时进行

-CPU资源
-浮动垃圾 需要预留空间
-内存碎片

G1 垃圾回收器   划多个区域 内存要大,小没有意义
化整为0,跨新生代和来年代
特点
空间整合,不会产生碎片
可预测的停顿 1000区域 每个区域10ms,1秒 就选出急需回收的100个区域
G1 GC模式
Yong GC
Mixed GC  Old 老年代  Humongous 大空间
全局并发标记
初始标记  --暂停
并发标记
最终标记 --暂停
刷选回收 --暂停

字面量:  文本信息,new String = ping  ping 就是字面量
符号引用: 类 类名 方法名称 方法描述符


虚拟机栈在分析
栈帧 :
局部变量表 存储8大基础类型  byte int short long char boolean double float

操作数栈

动态连接  多态:执行的是哪个方法,有的类中方法地址,编译无法确定,符号引用指向具体的

返回地址 正常退出 程序计数器  异常退出 异常处理表 栈帧不会保存

11.类加载机制
如果通过子类引用父类中的静态变量,只会触发父类的初始化,不会触发子类的初始化  
使用父类的方式不会触发初始化
调用一个常量,不会触发子 父类初始化
如果常量引用另外一个常量,触发父类初始化

加载
1.获取二进制流(通过类的全限定名获取)
2.字节流所代表的静态存储结构转化方法区运行时数据结构
3.入口:在内存区生产一个代表这个类class对象,作为方法区这个类各种数据访问入口

链接:
1)验证 格式验证 符号验证  是否符合JVM规范
2)准备 初始化  设置零值 例如 int 为0
3)解析 静态解析  动态解析   常量池方法区代号 解析成一个地址, 符号引用替换成直接引用为解析过程

初始化  类加载的最后一步,类构造器

使用

卸载

初始化5种情况
1.遇到 new getstatic 字节码
2.反射:reflect
3.初始化一个类,父类没有初始化,先触发父类初始化
4.JVM启动,指定执行主类(main方法的),JVM初始化
5.JDK动态语言支持,MethodHandle实例化类

12.什么时类加载器
静态方法是线程安全的,一个线程再初始化时,其他线程则是阻塞的。

类加载器
对于任意一个类,确定它的唯一性:类的全限定名+类加载器

研究:类加载器 jar 加密

13.双亲委派模型

双亲委派 自定义类加载器会一层一层往上面询问是否已经加载,如果加载自定类加载器则不会加载

分层次,保证java程序稳定性

tomcat 类加载器
两个应用怎么保证类的隔离性
每个应用都会new 一个WebAppClossLoader
WebAppClossLoader 没有实现父类的双亲委派

King老师  JVM课程,类加载机制 加密解密,怎么用实际的jar中。

14.JVM的溢出
栈溢出 StackOverFlowError  OutOfMemoryError

堆溢出
程序在申请的时候没有足够的内存
直接抛出
垃圾回收占用98%的资源,但是回收不足2%


-XX:MaxDirectMemorySize  最大直接空间

方法区溢出(垃圾回收效率低)
动态语言/CGLB/JSP

15内存泄漏
长生命周期的对象拥有短生命周期的引用
连接未关闭
内部类拥有外部类的引用

MAT 内存分析工具
浅堆(Shallow heap)
分配一个对象消耗的内存大小

深堆(Retained Heap)
分配一个对象,GC回收


JDK提供的工具
命令行工具
jps -m -q -l -v() 虚拟机进程状态

jstat -gc <pid>  250 10   250查一次 每次显示10条 虚拟机统计信息监控工具

jinfo  Java 配置信息工具
jinfo -flags <pid>
修改JVM参数(可变的参数才能改)
jinfo -flag +PrintGCDetails <pid>
HeapDumpOnOutOfMemoryError 内存溢出 日志默认关闭
HeapDumpPath  指定内存溢出日志

java -xx:+PrintFlagsFinal -version  查看jvm所有参数
manageable 才能修改
jmap  java 内存映射工具
jmap -dump:live,format=b,file=heap.bi <pid>

jhap 虚拟机堆转存储快照分析工具  非常消耗资源  不建议使用

jstack java堆栈跟踪工具

可视化工具
JConsole java监控和管理工具
Visualvm  多合一故障处理工具
插件 Visualvm GC

推荐策略
1.堆的空间设置,新生代的大小,吞吐量,新生代老年代的比例 比较大,大多数GC在新生代垃圾回收
2.老年代的大小选择,并发GC parNew CMS G1

JVM逃逸分析(对象逃不出方法的本身,对象不大)
+DoEscapeAnalysis 启动逃逸分析 (JVM默认开启)
+EliminateAllocations 标量替换(JVM默认开启)
+UseTLAB 本地线程默认分配缓存(JVM默认开启)
开启对象在栈上分配,不开启堆上分配

栈上分配

前端优化手段
减少请求次数
使用客户端缓存
使用压缩 gZIP  减少网络的请求量
资源文件加载顺序
减少Cookie传输
CDN加速:内容分发网络,将数据缓存在最近的地方
反向代理缓存
WEB组件分离

应用服务器性能优化
缓存
集群
异步

代码级别
选择合适的数据结构
选择更优的算法
编写更少的程序

资源复用
单例模式
池化技术


存储性能优化
尽量使用SSD
定时清除数据h或者按照数据的性质分开存放
结构集处理(数据库for)

编写高效优雅的代码
1.构造器太多怎么办

2.不许要实例的列构造器私有

3.不要创建不必要的对象

4.避免使用终结方法
finalizer 方法,要释放的资源不要放到这个方法,不一定执行
5.使用类和成员的可访问性最小化

6.使可变性最小化

7.优先使用复合胜过继承

8.接口优于抽象类

9.可变参数慎重使用
代码不优雅
10.返回零长度数组或者集合,不要返回null
Collections.EMPEY_NULL
11.优先使用标准异常
难管理

通用的程序设计
1.用枚举代替int常量
使用int常量,类变化需要重新编译。类编译加载在字节码中

2.局部变量的作用域最小化

3.精确计算,避免使用float 和double

4.当选字符串连接性能

5.控制方法的大小

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值