我正在参加「掘金·启航计划」
一 常用命令
1. jps 查看进程号
2. jinfo -flags 进程号
运行时参数
3. jmap
查看堆信息
二 Jvm体系结构
通过classLoader 将 class文件加载到运行时 数据区,然后通过执行引擎执行
1. 方法区 是规范(永久代是实现,原空间替代永久代)
类信息(包名、类名、接口信息、方法信息/静态变量、运行时常量池)
1.6 的时候常量池放在永久代[方法去]中
1.7 的时候常量池放在了堆中
1.8 移除了永久代的概念,变成了metaSpace
2. 堆:存放实例数据
2.1 新生代
Survivor 0
Survivor 1
Eden
2.2 老年代
3. Java栈
基本类型、对象引用/实例方法
线程私有,每个方法都有一个栈帧,栈帧存放的:局部变量表、操作数栈、动态链接、方法出口[上一个方法执行的地方]
4. 本地方法栈
5. 程序计数器
线程私有,指向下一条指令的地址
三 堆+栈+方法区的交互关系
栈中的对象引用 指向堆中的对象(实例数据、对象类型指针),堆中的对象类型指针指向方法区(Class 类型)中的对象类型数据
四 回收算法 和 GC收集器
1. 引用计数法[循环引用的问题]
2. 可达性分析[hotspot使用 root 节点]
2.1 作为GcRoot的对象:
虚拟机栈(局部变量表)的本地变量、静态变量
方法区的类属性
方法区的常量
本地方法栈的变量
3. 回收算法
3.1 标记清除
耗时、造成碎片空间
3.2 复制算法
复制耗时、浪费空间
存活对象较多的场合,只在新生代使用
3.3 标记压缩
耗时,存活对象较多的场合,适合老年代
4. 收集器
Serial: 单线程,简单高效、新生代复制算法
parNew:多线程
Parallel Scavenge: 新生代复制算法、吞吐量优先、自适应调节
Serial Old:老年代-标记整理算法
CMS:标记清除算法、并发收集、低停顿
G1:并发和并行、分代收集、标记整理算法、将堆分为多个大小相等的区[region]
5. 方法去的回收
5.1 回收废弃常量
5.2 没有被引用的常量
5.3 无用类
5.3.1 该类所有实例被回收
5.3.2 加载该类的classloader已被回收
5.3.3 class对象没有被引用
五 对象分配策略
1. 优先分配到 新生代 的 eden区
2. 老年代
提升:新生代的对象达到一定年龄
大对象直接分配到老年代
分配担保:eden 复制到 Survivor 区的时候,空间不足的情况下
动态分配:从最小年龄开始累加,对象综合大于 Survivor 50% 的时候,大于当前age的对象会分配到老年代
六 类加载器
1.启动类加载器,Bootstrap Classloader,加载JAVA_HOME\lib等底层类
2.扩展类加载器,Extension ClassLoader,加载\lib\ext,扩展类的加载
3.应用程序类加载器,Application ClassLoader,加载ClassPath中的类库
4.自定义类加载器,通过继承ClassLoader实现,加载我们自定义类的双亲委派模型
双亲委派模型:类加载时,先将请求委派给父类加载器加载完成,最底层、顶层Bootstrap ClassLoader加载器,如果父类无法加载,子类尝试自己加载。
双亲委派的好处:避免同一个类被多次加载,每个加载器只能加载自己范围的类。
5.类加载的生命周期:
类加载分三个步骤:加载、链接、初始化
流程为:
加载-》验证-》准备-》解析-》初始化 -》使用-》卸载
七 引用类型
1.强引用
强引用的对象不会被回收
2.软引用
只有在内存不足时被回收
3.弱引用
一定会被回收,生命周期就是下次垃圾回收之前
4.虚引用目的就是被回收的时候收到系统通止规,对其生存时间没有影响,决定于其他引用它是否被回收
八 对象的创建和结构
1. 步骤:
1.1 是否能在常量池中找到类符号引用,并检查是否被加载、解析、初始化,如果没有就进行类加载
1.2 分配内存
1.2.1 内存规整:指针碰撞
1.2.2 内存不规整:空闲列表
1.2.3 线程安全:本地线程分配TLAB,每个线程一个空间,互不干涉
2. 对象内存布局[结构]
2.1. 对象头[8个字节]
2.1.1 运行时数据区:哈希值、GC分代年龄、线程持有锁状态、偏向线程id
2.1.2 类型指针:确定是哪个类的实例
2.2. 实例数据 [4个字节]
代码中定义的各种类型字段的内容
相同宽度的数据放在一起
2.3. Padding(对齐填充)
4个字节的整数倍[占位符]
九 调优案例和实战
1. 问题
1.1 高性能硬件上的策略
问题:大内存使得GC停顿时间变长
控制Full GC 频率关键:大多数对象生命周期不应太长
1.2 集群间同步导致对象溢出
1.3 堆外内存溢出
1.3.1 Direct Memory
1.3.2 线程堆栈
1.3.3 socket缓冲区
1.3.4 虚拟机和GC
1.4 内部命令导致系统缓慢
调用外部命令,虚拟机会克隆一个和当前虚拟机一样的进程
1.5 服务端jvm 奔溃
异步调用另一个服务器上的服务,响应缓慢,导致请求积压
1.6 不恰当的数据结构导致内存占用过大
2 调优
2.1 类加载时间
2.2 编译
2.3 GC时间
2.3.1 扩大新生代 防止YGC
2.3.2 扩大老年代 防止FGC
java -XX:+PrintFlagsFinal -version
十 反射的三种方法
class.forname .class getClass
十一 什么是Java内存模型:
这里概念一定要明确,内存模型不是那个运行时数据图。
java内存模型全称为Java Memory Model,是java虚拟机为了java程序能够正常运行而制定的一套规范,规范中规定了JVM中的数据如何与RAM的数据进行交互。
java中分为主内存和工作内存,主内存中存放共享变量:实例字段、静态字段、数组对象元素。线程对共享数据的操作必须在工作内存中进行。这就是为什么需要java内存模型,其实就是对内存的管理,线程间的变量值的传递都需要通过主内存完成,不能相互之间访问变量。