java虚拟机
流程:helloworld.java----(javac编译)----helloworld.class-------(java运行)——JVM——机器码
JVM功能
*解释和运行
*内存管理
*即时编译(跨平台-慢一点)jit
(反复用到的代码 解释保存再内存里面)
jvm主要组成:
*类加载器,
*运行时数据区(jvm管理的内存),
*执行引擎(即时编译器,解释器,垃圾回收器)
字节码文件组成
工具:notepad++是不可以的 工具用 jclasslib
组成:基本信息,常量池,字段,方法,属性
基本信息
1, magic(魔数):确认是字节码文件(前缀固定0xcofe base)
2,主版本号 : (-44就等于jdk)-可以确定运行jdk和编译的版本是否一致
常量池
避免重复内容的重复定义,浪费空间
*常量池的数据都有一个编号
*字节码指令中提高编号引用常量池的过“符合引用”
方法
JVM:堆,栈,方法区
栈:是线程用的,(后进先出)(执行完就会释放)
*有多个栈帧组成,对应着每次方法调用时所占的内存
*每个线程只能有一个活动栈帧,对应正在执行的方法
递归会导致栈溢出
一个线程一个栈,一个方法一个栈帧
JVM调优
栈和栈帧
程序启动 在jvm的加载机制
1,将代码加载到 方法区(类加载)
2,栈启动mian主线程: 然后给线程的方法分配栈帧
new出来的对象放堆,方法执行完栈帧就会释放
!!!!
1线程启动就会分配栈,和程序计数器
2执行方法就会右栈帧(线程的栈帧是独立的)
3方法执行完栈帧就会释放,线程执行完栈就释放
程序计数器: *记录每个线程的执行到哪里-记录当前线程的状态-线程独有的)多线程切换用的
*由字节码执行引擎修改里面的内容
多线程的时候 操作系统的任务调度器 分配时间片
栈帧存的数据
1局部变量表(int = 1等)
2操作数栈(加减乘除操作时的数据空间,计算完就释放)
3动态链接、(每个方法内存地址,映射 元空间/方法区)
4方法出口(方法执行完 要继续执行main方法的下一个)
堆:new出来的对象---公共
栈(线程):放线程的--私有
栈里面的对象(保证地址)是指向堆的
本地方法栈:(用native修饰的方法-底层是用C++实现的)--私有
Thread类的本地方法 System类的本地方法
方法区(元空间):常理,静态变量,类信息---公共
方法区的对象也是指向堆的
JDK诊断工具
*Java VisualVM是JDK自带的基本调优工具之一
*jdk自带诊断命令
*arthas(阿里巴巴)诊断工具
。可以快速点位cpu高代码
。可以快速点位锁代码
。线上运行代码反编译
JVM堆的组成
1. 新生代(Young Generation):新生代是堆的一部分,用于存储新创建的对象。它又分为Eden区、Survivor区(通常有两个)。
- Eden区:新创建的对象首先分配在Eden区。
- Survivor区:当Eden区满时,存活的对象会被移到Survivor区。Survivor区一般有两个,分别称为From区和To区。存活的对象会在From区和To区之间进行复制,经过多次垃圾回收后(每次年龄+1),仍然存活的对象会被移动到老年代(15岁/6 CMS)。
2. 老年代(Old Generation):老年代用于存储长时间存活的对象。当对象经过多次垃圾回收后仍然存活,它们会被移动到老年代。(full GC老年代也回收)
对象年龄判断机制(如果对象大于Survivor 50%会直接放老年代)
3. 永久代(Permanent Generation):永久代用于存储类的元数据(metadata)和方法信息(method information)。在JDK 8之后,永久代被元空间(Metaspace)所取代。元空间不再位于堆中,而是位于本地内存中。
4. 堆外内存(Off-Heap Memory):堆外内存是指不受JVM堆管理的内存,通常由本地方法直接分配和释放。堆外内存包括直接内存(Direct Memory)和本地内存(Native Memory)。
需要注意的是,JVM的堆大小可以通过启动参数进行配置,例如-Xmx和-Xms参数用于设置堆的最大和初始大小。堆的大小对于应用程序的性能和内存使用有重要影响,需要根据具体应用场景进行合理配置。
堆-对象直接进入老年代
1,动态年龄判断:survivor从小到大累加年龄超过空间的50%(默认) ,后面的会直接晋升老年代
2 ,大对象直接接入 大对象就是需要大量连续内存空间的对象(比如:字符串、数组)。
复制很耗性能,
JVM参数XX:PretenureSizeThreshold 可以设置大 对象的大小,如果对象超过设置大小会直接进入老年代,不会进入年轻代,这个参数只在 Serial 和ParNew两个收集器下 有效。比如设置JVM参数:
-XX:PretenureSizeThreshold=1000000 (单位是字节) -XX:+UseSerialGC ,再执行下上
面的第一 个程序会发现大对象直接进了老年代
为什么要这样呢?(原因)
为了避免为大对象分配内存时的复制操作而降低效率。
(解决——加大Survivor区,并用G1分代收集)
3,老年代空间担保机制
From区和To区的作用如下:
1. 存储存活对象:在Minor GC过程中,存活的对象会被移动到Survivor区的From区。
2. 进行对象复制:在下一次Minor GC之前,存活的对象会从From区复制到To区。
3. 清空From区:复制完成后,From区会被清空,为下一次Minor GC做准备。
4. 交换From区和To区:在下一次Minor GC时,From区和To区会互换角色,即From区变为To区,To区变为From区。
通过交替使用From区和To区,Survivor区可以实现对象的复制和清理,以进行有效的垃圾回收。这种复制算法被称为"标记-复制"(Mark and Copy)算法,它可以有效地处理新生代中的对象,并减少内存碎片化的问题。
需要注意的是,Survivor区的大小可以通过JVM参数进行调整,以适应不同应用程序的需求。一般来说,Survivor区的大小应该合理设置,避免过小导致频繁的对象复制,或过大导致浪费内存空间。
垃圾回收
Minor GC 和 Full GC 垃圾回收会导致 stw(Stop the World")这个系统停顿
为什么要这样设计:不停止用户线程里面对象就会不断变化,实现简单,
JVM的可达性分析
JVM的可达性分析(Reachability Analysis)是垃圾回收的一种核心算法,用于确定哪些对象是可达的(reachable)或不可达的(unreachable),从而确定哪些对象应该被回收。
可达性分析的基本原理是从一组称为"GC Roots"的根对象开始,通过遍历对象引用链,标记所有与根对象直接或间接相连的对象为可达对象。而未被标记的对象则被认为是不可达的,即无法通过任何引用链访问到的对象。这些不可达对象将被垃圾回收器识别并回收,释放其占用的内存空间。
GC Roots包括以下几种类型的对象:
1. 虚拟机栈(VM Stack)中的引用对象。
2. 方法区(Method Area)中类静态属性引用的对象。
3. 方法区中常量引用的对象。
4. 本地方法栈(Native Method Stack)中JNI引用的对象。
通过从GC Roots出发进行可达性分析,JVM可以确定哪些对象是活动的,即仍然被引用和使用的对象,而哪些对象是不再使用的,可以被回收的对象。
可达性分析是现代垃圾回收器中常用的算法,它具有高效、准确的特点,并能够处理复杂的对象引用关系。通过可达性分析,JVM可以自动管理内存,释放不再使用的对象,从而提高系统的性能和资源利用率。
Minor GC
Minor GC(Minor Garbage Collection)是Java虚拟机(JVM)中的一种垃圾回收操作,主要针对新生代进行回收。新生代是Java堆内存中的一部分,用于存储新创建的对象。Minor GC的目标是清理新生代中的无用对象,以释放内存空间。
在Minor GC过程中,垃圾回收器会扫描新生代中的对象,并标记那些仍然存活的对象。然后,它会将存活的对象复制到Survivor区(通常是From区),同时清理掉无用的对象。在复制过程中,存活的对象会被移动到Survivor区的To区。最后,From区会被清空,为下一次垃圾回收做准备。
Minor GC通常发生在新生代中的Eden区(新对象的分配区域)空间不足时。当Eden区满了之后,会触发Minor GC来回收无用的对象,以便为新对象腾出空间。通常情况下,大部分对象在新生代中很快被回收,只有少部分对象会进入老年代(Old Generation)。
相比于Full GC(Full Garbage Collection),Minor GC的开销较小,回收的对象数量也较少。因此,Minor GC的执行时间通常较短,对应用程序的停顿时间影响较小。它是Java堆内存中垃圾回收的常见操作之一,用于保证新生代的内存空间的有效利用。
Full GC
Full GC(Full Garbage Collection)是Java虚拟机(JVM)中垃圾回收的一种操作,它是对整个堆内存进行回收的过程。Full GC会清理整个堆内存中的所有对象,包括年轻代和老年代。
Full GC通常是在进行一次完整的垃圾回收之前执行的,目的是回收所有不再被引用的对象,释放内存空间。Full GC的执行会导致应用程序的停顿,因为在此期间,所有的应用线程都会被暂停,直到垃圾回收完成。
Full GC通常发生在以下情况下:
1. 当堆内存空间不足时(如老年代太多),无法分配新的对象时,会触发Full GC来回收内存。
2. 当执行System.gc()方法或者调用Runtime.getRuntime().gc()方法时,可能会触发Full GC。
3. 当永久代(Permanent Generation)空间不足时(在JDK 8及之前的版本中),会触发Full GC来回收永久代。
Full GC的执行时间通常比部分垃圾回收(如年轻代的Minor GC)更长,并且会导致较长的停顿时间。因此,对于性能敏感的应用程序,需要合理配置堆内存大小,以减少Full GC的频率和影响。
总之,Full GC是Java虚拟机中对整个堆内存进行的垃圾回收操作,它会清理整个堆内存中的所有对象,包括年轻代和老年代,通常会导致较长的停顿时间。
JVM中常见的垃圾回收算法和策略包括以下几种:
1. 标记-清除算法(Mark and Sweep):该算法分为两个阶段,首先标记所有活动对象,然后清除未标记的对象。但是,标记-清除算法会产生内存碎片,可能会导致内存分配效率降低。
2. 复制算法(Copying):该算法将堆内存划分为两个相等大小的区域,每次只使用其中一个区域。当一个区域满时,将存活的对象复制到另一个区域,然后清除当前区域中的所有对象。复制算法消耗的时间较短,但会浪费一部分内存空间。
3. 标记-压缩算法(Mark and Compact):该算法首先标记所有活动对象,然后将活动对象向一端移动,最后清理掉边界以外的内存空间。标记-压缩算法消除了内存碎片,但可能会导致对象移动的开销较大。
4. 分代收集算法(Generational Collection):该算法根据对象的生命周期将堆内存划分为不同的代(Generation),如新生代(Young Generation)和老年代(Old Generation)。新生代中的对象生命周期较短,采用复制算法;而老年代中的对象生命周期较长,采用标记-压缩算法。
5. 并发标记清除算法(Concurrent Mark and Sweep):该算法允许垃圾回收器与应用程序并发执行,减少停顿时间。它通过在标记和清除阶段之间允许应用程序继续运行来提高性能。
6. G1收集器(Garbage-First Collector):G1收集器是一种面向服务端应用的垃圾回收器,它将堆内存划分为多个大小相等的区域(Region),通过并发标记、并发清除和并发整理来实现高效的垃圾回收。
这些垃圾回收算法和策略的选择取决于应用程序的性能需求和内存特点。JVM根据实际情况自动选择适当的垃圾回收器和算法来管理内存。
垃圾收集器
G1(Garbage First)收集器是Java虚拟机(JVM)中的一种垃圾收集器。它在JDK 7u4版本中首次引入,并在JDK 9及以后的版本中成为默认的垃圾收集器。G1收集器采用了分代收集和并发标记整理的方式,旨在提供可预测的停顿时间和高吞吐量的垃圾收集性能。
G1收集器的主要特点和优势包括:
1. 分代收集:G1收集器将堆内存划分为多个大小相等的区域(Region),每个区域可以是Eden区、Survivor区或Old区。这种分代的方式可以更好地适应不同对象的生命周期和内存使用模式。
2. 并发标记:G1收集器使用并发标记算法,在垃圾收集过程中,可以与应用程序线程并发执行标记阶段,减少垃圾收集对应用程序的影响。
3. 空闲区域优先回收:G1收集器的名字“Garbage First”即表示它优先回收垃圾最多的区域。这种策略可以最大程度地回收垃圾,提高垃圾收集的效率。
4. 可预测的停顿时间:G1收集器通过将堆内存划分为多个区域,并使用增量式的并发标记算法,可以控制垃圾收集的停顿时间。这对于要求低延迟的应用程序非常重要。
5. 自适应调节:G1收集器会根据堆内存的使用情况和垃圾收集的效果,动态地调整各个阶段的参数,以达到最优的垃圾收集性能。
G1收集器适用于大内存、多核处理器的应用场景,尤其是需要低延迟和高吞吐量的服务端应用程序。它在处理大堆内存和大量对象时表现出色,并且可以通过调整参数来满足不同应用程序的需求。