内存运行时数据区域
堆:存放对象实例,分为新生代(Eden)和老年代(From survivor,To survivor),多线程共享,Gc分代收集主要区域。
方法区(非堆):存放加载的类信息、常量、静态变量、编译后代码等数据,多线程共享,Gc分代收集。
栈:线程私有。
程序计数器:线程私有。
运行时常量池:存放类编译时生成的字面量和符号引用,属于方法区一部分。
直接内存:不属于虚拟机运行时数据区域。
TODO: 编写heap/stack/method area内存溢出代码
判断对象是否存活算法:可达性分析算法(不是引用计数算法),根据对象到GC Root是否有引用链相连!
可以作为GC Root对象有:
- 虚拟机栈(本地方法栈)所引用的对象。
- 方法区中static属性引用的对象(常量或静态变量).
死亡对象回收必须经过2次标记。
- 第一次标记判断是否需要执行finalize方法。
- 如果需要执行finalize方法,对象进入队列,执行finalize过程中,如果重新加入引用链,不会被回收;如不需要执行finalize方法,直接回收。
垃圾收集算法
- 标记-清除算法:效率不高,产生内存碎片
- 复制算法:使用内存缩小为原来一半,适合对象存活率较低的场合,以减少复制次数。在商业虚拟机中,用来回收新生代。HotSpot可用内存为90%,当survivor内存不够时,进入老年代。
- 标记-整理算法:基于标记-清除算法,存活对象向一端移动,清除其他内存。适用于虚拟机老年代内存。
- 分代收集算法:综合以上几种算法,对虚拟机划分为新生代和老年代,分别采用不同收集算法。
垃圾收集器
- Serial :单线程 Stop the World
- ParNew: 多线程 Stop the World
- Parallel Scavenge: 新生代,多线程,关注吞吐量
- Serial Old: 老年代 单线程 使用标记-整理算法
- Parallel Old: 多线程,老年代, 使用标记-整理算法
- CMS:以获取最短回收停顿时间为目标。(分为4步:初始标记,并发标记,重新标记,并发清除 )
- G1:特点是并行与并发 分代收集 空间整合 可预测的停顿 (分为4步:初始标记,并发标记,最终标记,筛选回收 )
内存分配与回收策略
- 对象优先在Eden分配
- 大对象直接进入老年代 (注意 :PretenuresizeThreshold只对Serial和ParNew收集器有效,以下代码是其他收集器)
-Xms20m -Xmx20m -Xmn10m -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=3145728
byte[] allocation;allocation=new byte[4*1048576];输出:Heap PSYoungGen total 9216K, used 5572K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) eden space 8192K, 68% used [0x00000000ff600000,0x00000000ffb71320,0x00000000ffe00000) from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000) to space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000) ParOldGen total 10240K, used 0K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) object space 10240K, 0% used [0x00000000fec00000,0x00000000fec00000,0x00000000ff600000) Metaspace used 2700K, capacity 4486K, committed 4864K, reserved 1056768K class space used 288K, capacity 386K, committed 512K, reserved 1048576K
- 长期存活的对象进入老年代
虚拟机性能监控工具
- jps 虚拟机进程名称及id
- jstat:见识虚拟机运行状态,以下监视java堆状况
- jinfo: Java配置信息工具,以下查看System.getProperties()内容
- jmap : Java内存映像
- Java堆栈跟踪
- Jconsole: 虚拟机内存、线程(运行状态,死锁等)等监控
- VisualVM:多合一故障处理工具
安装插件地址(默认的地址已失效)
BTrace: 动态加入调试代码
eclipse性能优化
以上是eclipse启动参数,其中--launcher.XXMaxPermSize只有eclipse检测到虚拟机是Sun公司的产品是,才把该参数传给虚拟机,可以通过VisualVM查看启动参数。
优化后,速度加快了不少。
Class类文件结构
魔数:u4 0xCAFEBABE
版本号:u4
常量池容量: u2 从1开始
常量:cp_info
访问标志:u2
类索引:u2
父类索引: u2
接口索引:u2
字段表集合
方法表集合
属性表集合
Class文件是平台无关的。
虚拟机类加载机制
初始化场景
- new
- 调用static属性(非final)
- 调用static方法
- 调用java.lang.reflect反射
- 初始化一个类时,先初始化父类
- 虚拟机启动,先初始化启动类
- 动态语言REF_getStatic等方法句柄对应类
加载:
1.根据类全限定名,获得二进制字节流
2.将二进制字节流代表的静态存储结构转化为方法区的运行时数据结构
3.在内存(HotSpot的方法区)生成代表这个类的Class对象,作为访问这个类的数据结构的入口
加载阶段1,可以使用自定义类加载器控制字节流获取方式
验证:验证字节流信息,是否符合虚拟机要求
准备
public static int value=123; 准备阶段过后,初始值0;
public staticfinal int value=123; 准备阶段过后,初始值123
解析:常量池内符号引用转变为直接引用
初始化
public static int value=123; 初始化阶段过后,初始值123;
类加载器分类
- 启动类加载器:c++实现,是虚拟机一部分,其他类加载器都独立于虚拟机之外,可以直接引用,加载<JAVA_HOME>\LIB\EXT目录
- 扩展类加载器:加载<JAVA_HOME>\LIB\EXT目录
- 应用程序类加载器:ClassLoader.getSystemClassLoader(),加载用户路径上的类,是应用程序默认类加载器
程序编译与代码优化
- 编译器优化
泛型擦除、自动装箱、拆箱、条件编译
- 运行期优化
Hotspot使用解释器和编译器共同工作
即时编译器编译的热点代码(使用基于计数器的热点探测来判断)包含
- 被多次调用方法
- 被多次执行循环体
即时编译器优化技术
- 方法内联:1.去除方法调用成本(建立栈帧)2.方法内联膨胀后,为其他优化建立基础
- 公共子表达式消除:(c*b)*12+a+(a+b*c)
- 数组边界检查消除
Java同步方式(3种)
- 互斥同步,Sychronized,Lock
- 非阻塞同步(CAS等依靠硬件支持,实现原子操作),AtomicInteger
- 阻止变量溢出,ThreadLocal