Java历史
2004.9 jdk1.5 tiger 自动装箱拆箱,泛型,,注解,枚举,变长参数,增强for循环 spring2.x spring4.x
2006 jdk1.6 javaee Javase Javame jdk6
- 提供脚本支持
- 提供编译api以及http服务器api
2009 jdk1.7 收购sun 74亿
2014 jdk1.8
2017 jdk1.9
2018 jdk10
java 技术体系
Java程序设计语言
java 虚拟机
class 类文件格式
Java API
第三方Java类库
Java8新特性
- 接口默认方法和静态方法
- lambda表达式和函数式编程
- dateAPI
- 重复注解
- nashorn JavaScript引擎
jvm可视化监控工具
jconsole.exe
在jdk/bin目录下,双击打开可运行,监控吗某个Java程序的状态
编写测试类观察jvm内存
JconsoleTest.java
package jconsole;
import java.util.ArrayList;
import java.util.List;
/**
* @author Veng Su [email protected]
* @date 2018/4/29 11:28
*/
public class JconsoleTest {
public static void main(String[] args) throws InterruptedException {
Thread.sleep(5000);
fill(1000);
}
private static void fill(int n) throws InterruptedException {
List<JconsoleTest> jconsoleTests =new ArrayList<JconsoleTest>();
for (int i=0;i<n;i++){
Thread.sleep(200);
jconsoleTests.add(new JconsoleTest());
}
}
}
jvm内存溢出
Main.java
import java.util.ArrayList;
import java.util.List;
/**
* @author Veng Su [email protected]
* @date 2018/4/29 10:39
*/
public class Main {
public static void main(String[] args) {
// 测试堆内存溢出
List<Demo> demoList=new ArrayList<Demo>();
while (true){
demoList.add(new Demo());
}
}
}
Demo.java
import java.util.ArrayList;
import java.util.List;
/**
* @author Veng Su [email protected]
* @date 2018/4/29 10:39
*/
public class Main {
public static void main(String[] args) {
// 测试堆内存溢出
List<Demo> demoList=new ArrayList<Demo>();
while (true){
demoList.add(new Demo());
}
}
}
抛出的异常:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:265)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:239)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:231)
at java.util.ArrayList.add(ArrayList.java:462)
at Main.main(Main.java:13)
jvm参数
导出堆内存
-XX:+HeapDumpOnOutOfMemoryError -Xms20m -Xmx20m
Java虚拟机的内存管理
运行时数据区
线程共享区
方法区
运行时常量池
Java堆
线程独立区
虚拟机栈
存放方法运行时所需的数据,称为栈帧
本地方法栈
为jvm调用到的native,即本地方法服务
程序计数器
记录当前线程执行到字节码的行号
程序计数器
- 如果线程执行的是Java代码,这个计数器记录的正在执行的虚拟机字节码指令的地址,如果正在执行的native方法,这个计数器的值为undefined
- 此区域是唯一一个在Java虚拟机规范中没有规定任何的OutOfMemoryError的情况的区域
Java虚拟机栈
这个描述的是Java方法执行的动态内存模型
栈帧:每个方法执行都会创建一个栈帧,伴随方法从创建到执行完成,用于存储局部变量表,操作数栈,动态链接,方法出口等
局部变量表:存放编译期已知的各种基本数据类型,引用类型,returnAddress类型
局部变量表的内存空间在编译期完成分配,在进入一个方法时,这个方法需要在帧分配多少内存是固定的,在方法运行期间是不会改变的虚拟机栈的大小
可能存在StackOverFlowErrorOutOfMemoryError内存不足,申请不到内存空间了
本地方法栈
Java虚拟机栈为虚拟机执行Java方法服务
本地方法栈为虚拟机执行native方法服务
Java堆
存放对象实例
垃圾搜集器管理的主要区域
新生代,老年代,Eden空间
申请不到空间同样抛出outofmemoryerror
方法区
存储虚拟机加载的类信息,常量,静态变量,及时编译器编译后的代码等数据
类信息:
类的版本
字段
方法
接口
方法区和永久代 Hotspot使用永久代实现方法区,两者不等价
垃圾回收在方法区的行为
异常的定义
申请空间失败抛出outofmemoryerror
运行时常量池
常量池相当于一个hashset存放这写常量,
而new 出来的实例肯定是放到堆内存中去
运行时常量和字节码常量的区别,运行时创建的常量和编译期创建的常量的区别
直接内存
对象的创建
给对象分配内存的方法
- 指针碰撞
- 空闲列表
可能会出现线程安全性问题
如何解决
线程同步 缺点:效率低
本地分配缓冲
对象的结构
header (对象头)
自身运行时数据(MarkWord)
哈希值 GC分代年龄 锁状态标志 线程持有的锁 偏向线程ID 偏向时间戳
类型指针、
instanceData
Padding
占位符填充8的整数倍的作用
对象的访问定位
- 使用句柄
定位句柄池,在找到对象地址 直接指针
直接找到对象地址 性能高 Hotspot使用直接地址定位其他虚拟机
Sun hotshot
Bea JRockit
IBM J9
虚拟机的发展
sun classic vm
- 世界山第一个商用虚拟机
- 只能使用纯解释器的方式执行java代码
exact vm
- exact memory management 准确试内存管理
- 编译器和解释器混合工作以及两级及时编译器
- 只在 Solaris平台发布
hotspot vm
kvm(kilobyte)
JRocket
BEA世界上最快的虚拟机
专注服务器端的应用
优势
垃圾搜集器
MissionControll服务套件 寻找运行时的内存泄露的问题
J9
IBM Technology for Java virtual machine
Azul vm
Liquid vm
Dalvik vm
不是Java虚拟机寄存器架构,非栈架构
Google的Microsoft jvm
只能运行在windows平台下taobaovm
深度定制
垃圾回收
如何判定对象为垃圾对象
- 引用计数法
在对象中添加一个引用计数器,当有地方引用这个对象的时候,引用计数器的值就加1,当引用失效的时候,计数器的值就减1
-verbose :gc -XX:+PrintGCDetails
打印垃圾回收的信息 - 可达性分析法
GCRoot对象
- 虚拟机栈
- 方法区类属性所引用的对象
- 方法区常量所引用的对象
- 本地方法栈所引用的对象
如何回收
回收策略
标记-清除算法
效率问题空间问题
复制算法
堆- 新生代
- Eden 伊甸园
- survivor 存活区
- Tenured Gen
- 老年代
虚拟机栈
本地方法栈
程序计数器- 新生代
标记-整理-清除算法
针对老年代分代收集算法
垃圾回收器
Serial
单线程Parnew
parallel scanvenge收集器
-XX:MaxGFPauseMillis 垃圾收集器停顿时间1
-XX:CGTimeRatio 吞吐量大小
复制算法(新生代收集器)多线程收集器
达到可控吞吐量
吞吐量:CPU运行代码的时间与CPU消耗的总时间的比值CMS收集器 current Mark sweep
- 工作过程
- 初始标记
- 并发标记
- 重新标记
- 并发清理
- 优点
- 并发收集
- 低停顿
- 缺点
- 占用大量CPU资源
- 无法处理浮动垃圾
- 出现current mode failure
- 空间碎片
- 工作过程
G1
内存分配
只要电脑运行内存大于2g,CPU核心是多核, 默认是ServerVM
可以看到我们的虚拟机是HotSpot
内存分配策略
优先分配到新生代的Eden区
VM option-verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -Xms20m -Xmx20m -Xmn10m -XX:SurvivorRatio=8
大对象直接进入到老年代
指定进入老年代的对象的内存大小
-XX:PretenureSizeThreshold长期存活的对象分配到老年代
-XX:MaxTenuringThreshold
具有年龄计数器。每次回收时存活,年龄加1.到达阈值就进入老年代中空间分配担保
如果内存空间不足,向担保借;
-XX:(+/-)HandlePromotionFailure
逃逸分析和栈上分配
通过逃逸分析,分析出没有逃逸的对象,直接在栈上分配空间。
什么是逃逸分析?
分析对象的作用域。如果对象只有在方法体内有效,则判定为没有逃逸。否则,为逃逸对象动态对象年龄判断
虚拟机工具
JPS
JAVA PROCESS STATUSJPS 名称: jps - Java Virtual Machine Process Status Tool
命令用法: jps options hostid
options:命令选项,用来对输出格式进行控制
hostid:指定特定主机,可以是ip地址和域名, 也可以指定具体协议,端口。
**功能描述: j**ps是用于查看有权访问的hotspot虚拟机的进程. 当未指定hostid时,默认查看本机jvm进程,否者查看指定的hostid机器上的jvm进程,此时hostid所指机器必须开启jstatd服务。 jps可以列出jvm进程lvmid,主类类名,main函数参数, jvm参数,jar名称等信息。
- 没添加option的时候,默认列出VM标示符号和简单的class或jar名称
- -p :仅仅显示VM 标示,不显示jar,class, main参数等信息.
- -m:输出主函数传入的参数. 下的hello 就是在执行程序时从命令行输入的参数
- -l: 输出应用程序主类完整package名称或jar完整名称.
- -v: 列出jvm参数, -Xms20m -Xmx50m是启动程序指定的jvm参数
- -V: 输出通过.hotsportrc或-XX:Flags=指定的jvm参数
- -Joption:传递参数到javac 调用的java lancher.
JSTAT
jstat命令可以类装载,内存,垃圾收集,jit编译。命令的格式如下:jstat [-命令选项] [vmid] [间隔时间/毫秒] [查询次数]
类加载统计
jstat -class
Loaded:加载class的数量 Bytes:所占用空间大小 Unloaded:未加载数量 Bytes:未加载占用空间 Time:时间
编译统计
jstat -compiler
Compiled:编译数量。 Failed:失败数量 Invalid:不可用数量 Time:时间 FailedType:失败类型 FailedMethod:失败的方法
垃圾回收统计
jstat -gccapacity
S0C:第一个幸存区的大小 S1C:第二个幸存区的大小 S0U:第一个幸存区的使用大小 S1U:第二个幸存区的使用大小 EC:伊甸园区的大小 EU:伊甸园区的使用大小 OC:老年代大小 OU:老年代使用大小 MC:方法区大小 MU:方法区使用大小 CCSC:压缩类空间大小 CCSU:压缩类空间使用大小 YGC:年轻代垃圾回收次数 YGCT:年轻代垃圾回收消耗时间 FGC:老年代垃圾回收次数 FGCT:老年代垃圾回收消耗时间 GCT:垃圾回收消耗总时间
新生代垃圾回收统计
jstat -gcnew
S0C:第一个幸存区大小 S1C:第二个幸存区的大小 S0U:第一个幸存区的使用大小 S1U:第二个幸存区的使用大小 TT:对象在新生代存活的次数 MTT:对象在新生代存活的最大次数 DSS:期望的幸存区大小 EC:伊甸园区的大小 EU:伊甸园区的使用大小 YGC:年轻代垃圾回收次数 YGCT:年轻代垃圾回收消耗时间
新生代内存统计
jstat -gcnewcapacity
NGCMN:新生代最小容量 NGCMX:新生代最大容量 NGC:当前新生代容量 S0CMX:最大幸存1区大小 S0C:当前幸存1区大小 S1CMX:最大幸存2区大小 S1C:当前幸存2区大小 ECMX:最大伊甸园区大小 EC:当前伊甸园区大小 YGC:年轻代垃圾回收次数 FGC:老年代回收次数
老年代垃圾回收统计
jstat -gcold
MC:方法区大小 MU:方法区使用大小 CCSC:压缩类空间大小 CCSU:压缩类空间使用大小 OC:老年代大小 OU:老年代使用大小 YGC:年轻代垃圾回收次数 FGC:老年代垃圾回收次数 FGCT:老年代垃圾回收消耗时间 GCT:垃圾回收消耗总时间
老年代内存统计
jstat -gcoldcapacity
OGCMN:老年代最小容量 OGCMX:老年代最大容量 OGC:当前老年代大小 OC:老年代大小 YGC:年轻代垃圾回收次数 FGC:老年代垃圾回收次数 FGCT:老年代垃圾回收消耗时间 GCT:垃圾回收消耗总时间
元数据空间统计
jstat -gcmetacapacity
MCMN:最小元数据容量 MCMX:最大元数据容量 MC:当前元数据空间大小 CCSMN:最小压缩类空间大小 CCSMX:最大压缩类空间大小 CCSC:当前压缩类空间大小 YGC:年轻代垃圾回收次数 FGC:老年代垃圾回收次数 FGCT:老年代垃圾回收消耗时间 GCT:垃圾回收消耗总时间
总结垃圾回收统计
jstat -gcutil
S0:幸存1区当前使用比例 S1:幸存2区当前使用比例 E:伊甸园区使用比例 O:老年代使用比例 M:元数据区使用比例 CCS:压缩使用比例 YGC:年轻代垃圾回收次数 FGC:老年代垃圾回收次数 FGCT:老年代垃圾回收消耗时间 GCT:垃圾回收消耗总时间
JVM编译方法统计
jstat -printcompilation
Compiled:最近编译方法的数量 Size:最近编译方法的字节码数量 Type:最近编译方法的编译类型。 Method:方法名标识。
JINFO
jinfo是jdk自带的命令,用来查看jvm的配置参数。通常会先使用jps查看java进程的id,然后使用jinfo查看指定pid的jvm信息
查看jvm的参数jinfo -flags process_id
查看java系统参数
jinfo -sysprops process_id
JMAP
JVM Memory Map命令用于生成heap dump文件,如果不使用这个命令,还可以使用-XX:+HeapDumpOnOutOfMemoryError
参数来让虚拟机出现OOM的时候自动生成dump文件。参数 option:选项参数,不可同时使用多个选项参数 pid:java进程id,命令ps -ef | grep java获取 executable:产生核心dump的java可执行文件 core:需要打印配置信息的核心文件 remote-hostname-or-ip:远程调试的主机名或ip server-id:可选的唯一id,如果相同的远程主机上运行了多台调试服务器,用此选项参数标识服务器 options参数 heap : 显示Java堆详细信息 histo : 显示堆中对象的统计信息 permstat :Java堆内存的永久保存区域的类加载器的统计信息 finalizerinfo : 显示在F-Queue队列等待Finalizer线程执行finalizer方法的对象 dump : 生成堆转储快照 F : 当-dump没有响应时,强制生成dump快照
-dump dump堆到文件,format指定输出格式,live指明是活着的对象,file指定文件名
-heap 打印heap的概要信息,GC使用的算法,heap的配置及使用情况,可以用此来判断内存目前的使用情况以及垃圾回收情况
-finalizerinfo 打印等待回收的对象信息,
-histo 打印堆的对象统计,包括对象数、内存大小等等。jmap -histo:live 这个命令执行,JVM会先触发gc,然后再统计信息 jmap -histo:live 24971 | grep com.yuhuo 查询类名包含com.yuhuo的信息 jmap -histo:live 24971 | grep com.yuhuo > histo.txt 保存信息到histo.txt文件
-permstat 打印Java堆内存的永久区的类加载器的智能统计信息。对于每个类加载器而言,它的名称、活跃度、地址、父类加载器、它所加载的类的数量和大小都会被打印。此外,包含的字符串数量和大小也会被打印。
-F 强制模式。如果指定的pid没有响应,请使用jmap -dump或jmap -histo选项。此模式下,不支持live子选项。
JHAT
JVM Heap Analysis Tool命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看。在此要注意,一般不会直接在服务器上进行分析,因为jhat是一个耗时并且耗费硬件资源的过程,一般把服务器生成的dump文件复制到本地或其他机器上进行分析。【内存分析】
参数 -J< flag > 因为 jhat 命令实际上会启动一个JVM来执行, 通过 -J 可以在启动JVM时传入一些启动参数. 例如, -J-Xmx512m 则指定运行 jhat 的Java虚拟机使用的最大堆内存为 512 MB. 如果需要使用多个JVM启动参数,则传入多个 -Jxxxxxx. -stack false|true 关闭对象分配调用栈跟踪(tracking object allocation call stack)。 如果分配位置信息在堆转储中不可用. 则必须将此标志设置为 false. 默认值为 true. -refs false|true 关闭对象引用跟踪(tracking of references to objects)。 默认值为 true. 默认情况下, 返回的指针是指向其他特定对象的对象,如反向链接或输入引用(referrers or incoming references), 会统计/计算堆中的所有对象。 -port port-number 设置 jhat HTTP server 的端口号. 默认值 7000。 -exclude exclude-file 指定对象查询时需要排除的数据成员列表文件(a file that lists data members that should be excluded from the reachable objects query)。 例如, 如果文件列列出了 java.lang.String.value , 那么当从某个特定对象 Object o 计算可达的对象列表时, 引用路径涉及 java.lang.String.value 的都会被排除。 -baseline exclude-file 指定一个基准堆转储(baseline heap dump)。 在两个 heap dumps 中有相同 object ID 的对象会被标记为不是新的(marked as not being new). 其他对象被标记为新的(new). 在比较两个不同的堆转储时很有用。 -debug int 设置 debug 级别. 0 表示不输出调试信息。 值越大则表示输出更详细的 debug 信息。 -version 启动后只显示版本信息就退出。
JSTACK
JCONSOLE
性能调优
常用思路
优化sql
监控CPU
监控内存
FULL GC 垃圾收集时间过长
解决方案
- 调整堆内存大小
问题:
- 不定期出现内存溢出,把堆内存加大也没用,导出内存信息没有任何信息.内存监控,也正常
处理思路:
- 控制变量法
- 硬件环境
- CPU
- 内存
- 软件环境
- 操作系统
- Java版本
- 容器
- 代码问题
- 硬件环境