第一章: Java虚拟机介绍
1.1 jvm分类
Sun Classic VM : 世界上第一款商用的Java虚拟机
Extract VM:准确试内存管理 编译器和解释器混合工作以及两级及时编译器
HotSpot VM:
KVM: kilobyte 简单,轻量, 高度可移植。 在手机平台运行
JRockit: BEA. 世界最快的Java虚拟机 优势 1.垃圾收集器。2. MissionControl 服务套件
J9:
Dalvik:
Microsoft JVM:
Azul VM Liquidj VM: 高性能的Java虚拟机
TaobaoVM:
1.2 Java虚拟机内存管理
线程共享区:方法区,Java堆
线程独占区:程序计数器, 虚拟机栈,本地方法栈
1.2.1 程序计数器
当前程序执行的字节码行号
程序计数器处于线程独占区
执行Java方法,计数器记录当前程序地址,native方法,记录undefined
此区域没有任何内存溢出
1.2.2 Java虚拟机栈
栈帧:
每个方法执行,都会创建一个栈帧
局部变量表:
存放编译期可知的各种基本数据类型,引用类型,returnAddress
大小:
递归的调用出现栈溢出:
stackoverflowError:栈溢出
OutOfMemery
1.2.3 Java虚拟机的本地方法栈
为虚拟机执行native方法的
1.2.4 Java虚拟机内存区域—堆区域
存放对象实例
垃圾收集器管理的主要区域
新生代。老年代
1.2.5 Java内存区域-方法区
1.存储虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据
2.方法区和永久代
3.垃圾回收在方法区的行为
4.异常的定义
OutOfMemeryError
1.2.6 Java内存区域-直接内存和运行时常量池
1.
String s1 = "abc";
String s3 = new String("abc");
s3.internal() 运行时常量
2.直接内存
能够分配堆外内存
1.2.7 对象在内存中的布局—对象的创建
第二章 垃圾回收机制-概述
2.1 如何判定对象为垃圾对象
1.引用计数法
2.可达性分析法
2.2 如何回收
1.回收的策略
标记-清除法
复制算法
标记-整理算法
分代收集算法
2.垃圾收集器
Serial
Parnew
Cms
G1
2.3 何时回收
2.3.1 引用计数法
垃圾回收-判断对象是否存活算法--引用计数法
在对象中添加引用计数器,当有地方引用这个对象的时候,引用计数器的值
就+1,当引用失效的时候,计数器的值就是-1
打印详细的GC信息
-verbose:gc
-xx:PrintGCDetails
代码:
Main m1 = new Main()
Main m2 = new Main()
m1.instance = m2;
m2.instance = m1;
m1 = null;
m2 = null;
2.3.2 可达性分析法
垃圾回收-判断对象是否存活算法--可达性分析法(主流)
作为GC Roots的对象
虚拟机栈
方法区的类属性所引用的对象
方法区中常量所引用的对象
本地方法栈中引用的对象
2.3.4 垃圾回收算法-标记清除算法
效率问题
空间问题
回收内存时导致内存不连续
2.3.5 垃圾回收算法-复制算法
内存资源极大浪费(内存空间只用一半)
堆:
新生代
Eden:伊甸园
Survivor:存活区
Tenured Gen:
老年代
方法区
栈
2.3.6 垃圾回收算法-标记整理清楚算法。分代收集算法
垃圾回收算法-标记整理清楚算法。分代收集算法
标记整理--回收老年代
分代收集算法--根据不同的分代采用不同的算法
2.3.7 垃圾收集器—Serial收集器(针对新生代)
最基本-发展最悠久
单线程垃圾收集器
2.3.8 垃圾收集器—parnew收集器
多线程垃圾收集器
2.3.9 垃圾收集器—Parallel收集器
复制算法(新生代收集器)
多线程收集器
可达到吞吐量:CPU用与运行用户代码的时间
与CPU消耗的总时间的比值
吞吐量 = (执行用户代码时间)/ (执行用户代码的时间 + 垃圾回收所占用的时间)
-XX:MaxGCPauseMillis: 垃圾收集器最大停顿的时间
-XX:GCTimeRatio : 吞吐量大小
(0,100)
2.3.10 垃圾收集器—CMS收集器
Concurrent Mark Sweep(并发标记清楚算法)
工作过程:
初始标记
并发标记
重新标记
并发清理
优点:
并发收集
低停顿
缺点:
占用大量的CPU资源
无法收集浮动垃圾
出现Concurrent Mode Failure
时间碎片
2.3.11 垃圾收集器—G1收集器
优势:
并行 并发
分代收集
空间整合
可预测的停顿
步骤:
初始标记
并发标记
最终标记
筛选回收
第三章 内存分配—概述
3.1 内存分配策略
内存分配策略
优先分配到Eden
大对象直接分配到老年代
长期存活的对象分配到老年代
空间分配担保
动态对象年龄判断
3.1.1 内存分配-Eden区域
打印详细的GC信息
-verbose:gc
-xx:PrintGCDetails
使用Serial算法
-XX:+UserSerialGC
指定堆内存大小(20M)
-Xms20M
-Xmx20M
新生代内存大小
-Xmn10M
限制Eden区的大小
-XX:SurvivorRation=8
3.1.2 内存分配-大对象直接进入老年代
指定对象的大小(如果大小这个值,则直接进入老年代)
--XX:PretenureSizeThreshold=6M
(指定大对象为6M)
3.1.3 内存分配-长期存活的对象进入老年代
--XX:MaxTenuringThreshold 15(默认值为)
3.1.4 内存分配-空间分配担保
-XX:+HandlePromotionFailure
3.1.5 内存分配-逃逸分析与栈上分配
逃逸分析:分析对象的作用域
引用的作用域仅在当前方法中有效,没有发生逃逸
引用成员变量的值,发生逃逸
代码:
3.2 虚拟机工具介绍
jps
jstat
jinfo
jmap
jhat
jstack
jconsole
3.2.1 虚拟机工具-jps
Java Process status
本地虚拟机唯一ID local virtual machine ID
jps -l 类的全名
jps -m 接收命令行参数
jps -v 接收虚拟机参数
3.2.2 虚拟机工具-jstat
类加载。内存。垃圾收集 git编译信息
jstat -gcutil 6692
jstat -gcutil 6692 1000 10
S0:Survivor0
S1:Survivor1
E:Eden
O:Old
M:Meta
CCS:压缩类空间
YGC:年轻代发生的GC次数
YGCT:年轻代发生的GC次数的时间
FGC:
FGCT:
GCT:
元空间本质和永久代本质类似
元空间不在虚拟机,在直接内存中
**
3.2.3 虚拟机工具-jinfo
实时查看和调整虚拟机的各项参数
jinfo -flag UseSerialGC
-XX:
3.2.4 虚拟机工具-jmap
导出进程的快照信息 显示堆中的信息
jmap -dump:format=b,file=~/a.bin pid
jmap histo
-XX:+HeapDumpOnOutOfMemoryError
3.2.5 虚拟机工具-jhat (Jvm heap Analysis Tool)
将dump的heap信息拷贝本地分析
jmap -dump:format=b,file=d:\a.bin pid
jhat d:\a.bin
OQL的使用
3.2.6 虚拟机工具-jstack
生成线程栈快照
目的:导致线程长时间停留原因
jstack -l pid
Map<Thread,StackTraceElemnt[]> m = Thread.getAllStackTraces();
for( Map.Entry<Thread,StackTraceElemnt[]> en : m.entry.set) {
}
3.2.7 可视化虚拟机工具-jconsole内存监控
3.2.8 可视化虚拟机工具-jconsole线程监控
3.2.9 可视化虚拟机工具-jconsole线程死锁监控
第四章 性能调优
知识
工具
数据
经验
性能调优--案例1
性能调优--案例2
Class文件简介和发展历史
Helle.java
javac Helle.java
生成Helle.class
运行Java程序
java Helle
第五章 class文件结构
第六章 类加载过程
6.1 类加载的过程—加载
通过一个类的全限定名来获取定义此类的二进制流
将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
在内存中生成一个代表这个类的Class对象,作为这个类的各种数据的访问入口
加载源:
文件
class文件
jar文件
网络
计算生成一个二进制流
由其他文件生成
数据库
6.2 类加载的过程—验证
为了确保class文件的字节流中包含的信息复合当前虚拟机的要求并不回危害虚拟机自身的安全
文件格式验证
元数据验证
字节码验证
符号引用验证
6.3 类加载的过程—准备
为类变量分配内存并设置变量的初始值(给变量初始化为默认的值)
这些变量使用的内存都将在方法区中进行分配
6.4 类加载的过程—解析
虚拟机将常量池中的符号引用替换为直接引用的过程
类或者接口的解析
字段解析
类方法解析
接口解析
6.5 类加载的过程—初始化
类加载的最后一步
执行<clinit>方法的过程
4.如果多个线程同时初始化一个类,只有一个线程执行这个类的init方法
问题:多线程执行static 代码块时,容易阻塞
6.6 类加载器
1.通过一个类的全限定名来获取描述
2.只有被同一个类加载器加载才相等
6.6.1 载器分类
启动类加载器
由C+实现,时虚拟机的一部分。lib
扩展类加载器
lib/ext
应用程序加载器
加载用户类路径
自定义类加载器
优势:高度的灵活
通过自定义类加载器可以实现热部署
代码加密
6.6.2 自定义类加载器
1.定一个类,继承classloader
2.重写loadClass方法
3.实例
ClassLoader mycl = new ClassLoader(){
public Class<?> loadClass(String name) throws {
String filename = name.substring(name.lastIndexOf(".") + 1) + ".class";
InputStream ins = getClass().getResouceAsStream(fileName);
if ( ins == null ) {
return super.loadClass(name);
}
byte [] buff = new byte[ins.available()];
ins.read(buff);
return defineClass(name, buff, 0, buff.length);
}
}
Class c = mycl.loadClass("com.root.Test").newInstance();