目录
Jstat(监视虚拟机各种状态信息类加载,内存,垃圾收集,jit编译信息,通过进程号来查看)
Javc技术体系:
- Java程序设计语言
- JavaSE stardard edition 桌面应用级别的java平台 提供java核心的API
- JavaME 移动终端的API
- JavaEE 企业级的应用
- 个硬件平台上的Java虚拟机
- Class文件格式
- Java API
- 第三方的Java类库
平台:指的是操作系统(Windows,Linux,Mac)
跨平台:Java程序可以在任意操作系统上运行,一次编写到处运行
原理:实现跨平台需要依赖Java的虚拟机 JVM (Java Virtual Machine)
jdk包含和jre;jre包含了jvm;
JVM是java虚拟机(JVM Java Virtual Machine),java字节码文件(java程序)需要运行在虚拟机上,不同平台有自己的虚拟机,因此java语言可以跨平台.
内存结构(5大内存区域):
- 程序计数器(线程独占去)
- 每个线程区都有自己的程序计数器用来控制代码的执行顺序
2.java虚拟机栈中的栈桢和局部变量表(存放在线程共享区)
- 每个方法执行都会创建一个栈桢进入栈中,先创建的栈桢后出栈,后创建的栈桢先出栈;(先进后出)
- 局部变量表存放编译期可知的基本数据类型和引用类型对象,分配的内存是固定的,在方法执行的时间是不会改变局部变量表内存的大小的,如果执行的方法超过分配的大小就会造成内存溢出错误
3.本地方法栈和java虚拟机栈是相似的,本地方法栈是为虚拟机执行本地方法,虚拟机栈为java执行java方法
4.方法区(和堆一样存储在线程共享区)
- 将类的信息,常量,静态变量,和几十编译的代码数据;
- 垃圾回收在方法区回收效率很低,一般很少进行垃圾回收(堆常量池的回收和)
- 异常的定义(申请内存区域申请失败会抛出异常OutOfMenoryError)
5.堆区(存储对象实例)
对象的创建:
根据new的参数在常量池中定义一个类的符号引用,如果没找到这个符号引用,说名类没有被引用,说明类没有被加载,则进行加载,解析和初始化,虚拟机为对象分配内存将分配的存初始化为o,调用对象的init()方法
- 给对象分配内存(指针碰撞每次创建指针向后运动;;空闲列表为对象分配空闲的区域)
- 线程安全问题(针对每一个线程分配指定的区域或者加锁同步)
- 初始化对象
- 执行构造方法
- 对象的结构
- Header (对象头)
- 自身运行时的数据
- 类型指针
2.InstaceData(实例对象数据)
垃圾回收机制(主要针对堆内存):
-verbose:gc -XX:+PrintGCDetails 打印垃圾回收机制的日志信息使用方法;
1.如何判定对象为垃圾对象
- 引用计数法
- 在对象中添加一个引用计数器,当有地方引用就加1,当引用失效是就减1
- 可达性分析法
- 通过gc根节点对栈区的所有对象进行搜索,通过判断这个对象有引用链,没有被引用就会被回收
2.如何回收
- 回收的策略
- 标记清楚算法
- 两个过程第一个通过判断进行垃圾对象标记,然后对标记进行垃圾回收
- 有两个问题:1.效率问题2.空间问题
2.复制算法
- 解决上面两个问题对新生代内存进行收集的算法,每次回收内存比较多,存货下来的会放到存活区,当存活区内存溢出的时候,又会放到老年待
3.标记整理算法
4.复制分带搜集算法;根据栈区不同的区域进行不同回收的算法;
- 常见的垃圾回收器(用来提升垃圾回收的性能)
- serial垃圾收集器(一般在客户端使用:桌面应用)
最基本发展最悠久的,是单线程的,没有线程开销,单个效率高,整体效率低!
2.parnew(一般在服务端使用:)
多线程收集,针对新生代内存
3.Parallel Scavenge 收集器
采用复制算法主要针对新生代内存,多线程收集器
可以控制程序吞吐量的大小
吞吐量: 执行代码时间占比 和垃圾回收占比 比如 (0,100) 设置99 那么执行代码时间占比99%
通过下面两个参数设置根据应用场景不同来设置:
4.Cms(并发收集器,用于回收老年代内存,采用标记清楚算法)
优点:1.并发收集2.停顿地 缺点: 1.占用CPU资源2.无法处理浮动垃圾()3.产生空间碎片4.会出现内存不足
5.G1收集器(面向服务端的收集器,最牛)
优势:1.并行和并发2.分代收集(分程一块一块内存区域不在分成新生代和老年代来分)
3.空间整合 4..可预测的停顿(减少停顿)
3.何时回收
内存分配:
创建对象优先分配到eden(新生代),大对象可能直接分配到老年代,长期存活的对象分配到老年代,空间分配担保,动态的年龄判断
-verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC (指定选用seria垃圾回收器) 打印详细的垃圾搜集信息
运行代码如下
public class JvmDemo {
public JvmDemo() {
byte [] bs = new byte[1024*20*1024];
}
public static void main(String[] args) {
byte [] b =new byte [4*1024*1024];
System.gc();
}
}
控制台输出如下:
Heap
def new generation total 6144K, used 55K [0x00000000fec00000, 0x00000000ff2a0000, 0x00000000ff2a0000)
(eden区域,创建对象优先在eden分配)eden space 5504K, 1% used [0x00000000fec00000, 0x00000000fec0dda0, 0x00000000ff160000)
from space 640K, 0% used [0x00000000ff160000, 0x00000000ff160000, 0x00000000ff200000)
to space 640K, 0% used [0x00000000ff200000, 0x00000000ff200000, 0x00000000ff2a0000)
tenured generation total 13696K, used 4722K [0x00000000ff2a0000, 0x0000000100000000, 0x0000000100000000)
the space 13696K, 34% used [0x00000000ff2a0000, 0x00000000ff73c878, 0x00000000ff73ca00, 0x0000000100000000)
Metaspace used 2787K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 298K, capacity 386K, committed 512K, reserved 1048576K
如果分配内存比较大会分配到 老年代区域!通过代码可测试
长期存活的对象可进入老年代,通过参数 -XX:MaxTenuringThreshold 来指定回收的次数界限(就是说垃圾回收其回收一次存活下来+1,达到值之后还存活那么就进入老年区 )
空间分配担保:
-XX:+HandlePromotionFailure - 号来禁止空间分配担保 + 号来开启空间分配担保
逃逸分析与栈上分配内存:
逃逸分析:分析出对象的作用域,如果说对象定义在方法的方发体内并且只针对本作用域外部无法改变,就认为对象没有逃逸,就分配在栈上去;
代码实例:
public class JvmDemo {
private JvmDemo obj;
/*方法返回JvmDemo 发生逃逸*/
public JvmDemo JvmDemo() {
return obj==null ? new JvmDemo() : obj;
}
/*方法为成员属性进行赋值 发生逃逸*/
public void setObj() {
this.obj = new JvmDemo();
}
/*引用成员变量的值发生逃逸*/
public void JvmDemo2() {
JvmDemo obj2 = JvmDemo();
}
/*作用域仅在当前方法有效没有发生逃逸*/
public void JvmDemo3() {
JvmDemo obj3 =new JvmDemo();
}
}
性能监控工具:
jps(查看java线程的状态,类似任务管理器)
https://www.cnblogs.com/tulianghui/p/5914535.html 各种指令详解
能够查看本地虚拟机唯一进程id
m 运行时传入主类的参数
v 虚拟机参数
L 运行主类的全名 或者jar包的全名
mlv 全部的参数
Jstat(监视虚拟机各种状态信息类加载,内存,垃圾收集,jit编译信息,通过进程号来查看)
https://www.cnblogs.com/lizhonghua34/p/7307139.html 各种指令详解
后面加上毫秒值 会每隔指定的时间监控一次
jstat -gcutil 17052 1000 每一秒打印一次
jinfo(实时查看调整虚拟机各项参数)
https://blog.csdn.net/winwill2012/article/details/46336839 命令详情地址
jmap
https://blog.csdn.net/zhaozheng7758/article/details/8623530 命令详情地址
转储堆快照信息 jmap -dump:format=b,file=地址 进程号
jhat
https://blog.csdn.net/gtuu0123/article/details/6039474
jstack
https://www.cnblogs.com/kongzhongqijing/articles/3630264.html
可视化虚拟机工具
JConsole(对内存和线程监控)
VisualVm
性能调优案例:
类的文件结构:
类的加载机制(过程类加载器):
字节码执行引擎:
虚拟机编译及运行时优化:
-
解决内存溢出的问题:
这个操作会在项目下生成一个.hprof文件
使用eclipse插件来打开首先下载地址http://www.eclipse.org/downloads/download.php?file=/mat/1.8/rcp/MemoryAnalyzer-1.8.0.20180604-win32.win32.x86_64.zip
解压后双击打开exe程序
会精准定位到指定的包中的方法
测试代码
public class Demo{}
public class JvmDemo {
public static void main(String [] args) {
List<Demo> list = new ArrayList<Demo>();
while(true) {
list.add(new Demo());
}
}
}
1.添加jvm参数 到处快照内存 -XX:+HeapDumpOnOutOfMemoryError
2.-Xms120m -Xmx120m 限制内存大小的参数
3.使用 MemoryAnalyzer 插件打开生成的.prof文件来精准定位找到问题