一 内存区域及对象创建
1.1 运行时数据区
jdk7默认栈大小为1M java -XX:+PrintFlagsFinal -version | grep -i 'stack' 可查看与stack相关信息
1.2 分配对象空间
慢速分配 重点在是否用TLAB和“指针碰撞”
1.3 TLAB
JVM在内存新生代Eden Space中开辟了一小块线程私有的区域,称作TLAB(Thread-local allocation buffer)。默认设定为占用Eden Space的1%。在Java程序中很多对象都是小对象且用过即丢,它们不存在线程共享也适合被快速GC,所以对于小对象通常JVM会优先分配在TLAB上,并且TLAB上的分配由于是线程私有所以没有锁开销。因此在实践中分配多个小对象的效率通常比分配一个大对象的效率要高。也就是说,Java中每个线程都会有自己的缓冲区称作TLAB(Thread-local allocation buffer),每个TLAB都只有一个线程可以操作,TLAB结合bump-the-pointer技术可以实现快速的对象分配,而不需要任何的锁进行同步,也就是说,在对象分配的时候不用锁住整个堆,而只需要在自己的缓冲区分配即可。
1.4 Mark Word
二 垃圾回收
2.1 如何确定对象已死?
引用计数算法
不用!问题是有对象循环引用的问题
可达性分析算法
用GC Roots 作为起点,当一个对象到GC Roots没有任何引用链相连,就可回收,枚举GCRoots会导致 “ stop the world ” 以下对象被标记成Root:
- Class: 由系统类加载器(system class loader)加载的类,它们不能被卸载。由自定义的类加载器加载的类不是Root,除非相应的java.lang.Class的实例是其它类型的Root
- Thread: 活着的线程
- Stack Local:Java方法的参数或者本地变量
- JNI Local: JNI方法的参数或者本地变量
- Monitor Used:同步用的监控器
- Held by JVM: JVM自己持有的对象,比如系统类加载器,一些异常等
2.2 对象引用
• 强引用:new 出来的一般对象,只要引用在就不会被回收
• 软引用: 将要发生内存溢出之前回收
• 弱引用: 生存到下一次垃圾收集发生之前
• 虚引用:目的是对象被收集器回收时收到一个系统通知
2.3 垃圾收集算法
复制-Cpoying:
- 将内存分成两块,一块用完了,将可用的放到另一块,第一块全部回收,缺点,只能用一半的内存代价太高。
- 在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,选用:复制算法在老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清除”或者“标记-整理”算法来进行回收。
标记清除-Mark-Sweep:
先标记后清除 缺点:1 效率不高 2 内存碎片导致提前触发回收
标记整理-Mark-Compact:
将存活的对象向一端移动,直接清理掉边界以外的内存
分代收集算法-Generational Collection
2.4 算法实现
hotspot的算法实现 ,如何发起回收
• 枚举根节点
• 安全点 safepoint
• 安全区域safeRegion
2.5 垃圾收集器
有关这一节的内容我在前文 JVM G1(Garbage First)垃圾收集器浅析 中都有写到,这里就不赘述了。
2.6 内存分配
Minor GC
- 存活对象会反复在S0和S1之间移动,当对象从Eden移动到Survivor或者在Survivor之间移动时,对象的GC年龄自动累加,当GC年龄超过默认阈值15时,会将该对象移动到老年代,可以通过参数-XX:MaxTenuringThreshold 对GC年龄的阈值进行设置。
- 长久存活的直接进入老年代,默认年龄15岁
- 大对象直接进入老年代,所谓大对象就是大量连续内存空间的对象。-XX:PretenureSizeThreshold参数,令大于这个值的对象直接进入老年代
- Minor GC触发条件:当Eden区满时,触发Minor GC。
空间分配担保
- 当 JVM 无法为一个新的对象分配空间时会触发 Minor GC,比如当 Eden 区满了就会进行MinorGC,在MinorGC之前 检查老年代最大连续可用空间是否大于新生代所有对象空间总和。
2.7 Full GC
什么时候发产生?
- System.gc()方法的调用
- 老年代代空间不足
- 永生区空间不足
- CMS GC时出现promotion failed和concurrent mode failure
- 统计得到的Minor GC晋升到旧生代的平均大小大于老年代的剩余空间
- 堆中分配很大的对象
2.8 回收方法区
主要是两部分 • 废弃常量 • 无用的类
2.9 Sto The World
stop the world (STW) 不管是新生代老生代都会产生STW,重点是时长多久
三 性能监控与故障处理工具
- jps(JVM Process Status):虚拟机进程状况工具 显示虚拟机进程 jps -l
- jstat(JVM Statistics Monitoring Tool):监控虚拟机各种运行状态
- jinfo(Configuration Info for Java):java配置信息工具
- jmap(Memory Map for Java) 堆转储快照
- jstack(Stack Trace for Java) java堆栈跟踪工具
- 监控工具:
• jconsole
• visualVM
• BTrace 动态日志跟踪:可以通过HotSpot虚拟机的HotSwap的技术动态加入 原来不存在的调试代码。
四 class 文件
一文让你明白java字节码(https://www.jianshu.com/p/252f381a6bc4),这篇文章写的很明白!
五 虚拟机类加载机制
虚拟机把描述类的数据从Class文件加载 到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的JAVA类型,这就是虚拟机的类加载机制。
加载 : 一个类必须与类加载器一起确定唯一性 • 加载阶段完成后,虚拟机外部的二进制字节流就按照虚拟机所需的格式存储在方法区之中。
验证:可以使用 -Xverify:none参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。
准备:准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。
5.1 类加载器
虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到JAVA虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为“类加载器”
比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义
5.2 双亲委派模型
- 启动类加载器(Bootstrap ClassLoader),加载<JAVA_HOME>\lib 目录中的类库
- 扩展类加载器(Extension ClassLoader),加载<JAVA_HOME>\lib\ext目录中的类库
- 应用程序类加载器(Application ClassLoader),加载用户类路径(ClassPath)上所指定的类库
如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即ClassNotFoundException),子加载器才会尝试自己去加载
5.3 破坏双亲委派模型
- JNDI、JDBC等
- OSGI
5.4 SPI
SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制。目前有不少框架用它来做服务的扩展发现, 简单来说,它就是一种动态替换发现的机制, 举个例子来说, 有个接口,想运行时动态的给它添加实现,你只需要添加一个实现。具体是在JAR包的"src/META-INF/services/"目录下建立一个文件,文件名是接口的全限定名,文件的内容可以有多行,每行都是该接口对应的具体实现类的全限定名
java的spi 的简单应用(https://www.cnblogs.com/huzi007/p/6679215.html)