深入java虚拟机(极简版读书笔记)(一)

简介: JVM在内存新生代Eden Space中开辟了一小块线程私有的区域,称作TLAB(Thread-local allocation buffer)。默认设定为占用Eden Space的1%。在Java程序中很多对象都是小对象且用过即丢,它们不存在线程共享也适合被快速GC,所以对于小对象通常JVM会优先分配在TLAB上,并且TLAB上的分配由于是线程私有所以没有锁开销。因此在实践中分配多个小对象的效率通常比分配一个大对象的效率要高。

一  内存区域及对象创建


1.1 运行时数据区


35.jpg


jdk7默认栈大小为1M java -XX:+PrintFlagsFinal -version | grep -i 'stack' 可查看与stack相关信息


1.2 分配对象空间


36.png


慢速分配 重点在是否用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


37.jpg


二 垃圾回收


38.jpg


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),这篇文章写的很明白!


40.jpg


五 虚拟机类加载机制


    虚拟机把描述类的数据从Class文件加载 到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的JAVA类型,这就是虚拟机的类加载机制。


41.png


加载 : 一个类必须与类加载器一起确定唯一性 • 加载阶段完成后,虚拟机外部的二进制字节流就按照虚拟机所需的格式存储在方法区之中。


验证:可以使用 -Xverify:none参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。


准备:准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。


5.1 类加载器

   

     虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到JAVA虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为“类加载器”

 

     比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义



5.2 双亲委派模型


43.jpg


  • 启动类加载器(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)



相关文章
|
1月前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
6月前
|
存储 算法 安全
Java面试题:Java内存模型及相关知识点深度解析,Java虚拟机的内存结构及各部分作用,详解Java的垃圾回收机制,谈谈你对Java内存溢出(OutOfMemoryError)的理解?
Java面试题:Java内存模型及相关知识点深度解析,Java虚拟机的内存结构及各部分作用,详解Java的垃圾回收机制,谈谈你对Java内存溢出(OutOfMemoryError)的理解?
82 0
|
3月前
|
存储 算法 Java
Java虚拟机(JVM)的内存管理与性能优化
本文深入探讨了Java虚拟机(JVM)的内存管理机制,包括堆、栈、方法区等关键区域的功能与作用。通过分析垃圾回收算法和调优策略,旨在帮助开发者理解如何有效提升Java应用的性能。文章采用通俗易懂的语言,结合具体实例,使读者能够轻松掌握复杂的内存管理概念,并应用于实际开发中。
|
4月前
|
存储 算法 Java
深入解析 Java 虚拟机:内存区域、类加载与垃圾回收机制
本文介绍了 JVM 的内存区域划分、类加载过程及垃圾回收机制。内存区域包括程序计数器、堆、栈和元数据区,每个区域存储不同类型的数据。类加载过程涉及加载、验证、准备、解析和初始化五个步骤。垃圾回收机制主要在堆内存进行,通过可达性分析识别垃圾对象,并采用标记-清除、复制和标记-整理等算法进行回收。此外,还介绍了 CMS 和 G1 等垃圾回收器的特点。
138 0
深入解析 Java 虚拟机:内存区域、类加载与垃圾回收机制
|
5月前
|
Java
Java常见JVM虚拟机指令(47个)
Java常见JVM虚拟机指令(47个)
80 3
Java常见JVM虚拟机指令(47个)
|
6月前
|
监控 Oracle Java
(一)JVM成神路之初识虚拟机 - 探寻Java虚拟机的前世今生之秘
JVM(Java Virtual Machine)Java虚拟机的概念大家都不陌生,Java之所以可以做到“一次编译,到处运行”的跨平台性,其根本原因就在于JVM。JVM是建立在操作系统(OS)之上的,Java虚拟机屏蔽了开发人员与操作系统的直接接触,我们在通过Java编写程序时,只需要负责编写Java代码即可,关于具体的执行则会由JVM加载字节码后翻译成机械指令交给OS执行。
106 1
|
5月前
|
Java 数据安全/隐私保护 Windows
【Azure Developer】使用Java代码启动Azure VM(虚拟机)
【Azure Developer】使用Java代码启动Azure VM(虚拟机)
|
5月前
|
存储 Java API
【Azure Developer】通过Azure提供的Azue Java JDK 查询虚拟机的CPU使用率和内存使用率
【Azure Developer】通过Azure提供的Azue Java JDK 查询虚拟机的CPU使用率和内存使用率
|
6月前
|
存储 Ubuntu Java
【Linux】已解决:Ubuntu虚拟机安装Java/JDK
【Linux】已解决:Ubuntu虚拟机安装Java/JDK
211 1
|
6月前
|
监控 算法 Java
深入理解Java虚拟机:内存管理与性能优化
在Java的世界里,虚拟机(JVM)是幕后的守护者,它默默地支撑着每一个字节码的运行。本文将揭开JVM的神秘面纱,探讨其内存管理机制及如何通过调优提升应用性能。从堆内存的分配到垃圾回收的策略,再到实践中的性能监控与调优技巧,我们将一同走进JVM的内部世界,学习如何让我们的Java程序跑得更快、更稳。