网站如何上线,wordpress账户密码忘记,wordpress hierarchy,网站空间名JVM架构图分析
下图#xff1a;参考网络书籍#xff0c;如有侵权请见谅 #xff08;想了解Hadoop内存溢出请看#xff1a; Hadoop内存溢出(OOM)分类、参数调优化#xff09; JVM被分为三个主要的子系统
#xff08;1#xff09;类加载器子系统#xff08;2#xff0…JVM架构图分析
下图参考网络书籍如有侵权请见谅 想了解Hadoop内存溢出请看 Hadoop内存溢出(OOM)分类、参数调优化 JVM被分为三个主要的子系统
1类加载器子系统2运行时数据区3执行引擎
1. 类加载器子系统
Java的动态类加载功能是由类加载器子系统处理。当它在运行时不是编译时首次引用一个类时它加载、链接并初始化该类文件。
1.1 加载
类由此组件加载。启动类加载器 (BootStrap class Loader)、扩展类加载器(Extension class Loader)和应用程序类加载器(Application class Loader) 这三种类加载器帮助完成类的加载。
1. 启动类加载器 – 负责从启动类路径中加载类无非就是rt.jar。这个加载器会被赋予最高优先级。
2. 扩展类加载器 – 负责加载ext 目录(jre\lib)内的类.
3. 应用程序类加载器 – 负责加载应用程序级别类路径涉及到路径的环境变量等etc.
上述的类加载器会遵循委托层次算法Delegation Hierarchy Algorithm加载类文件。
1.2 链接
1. 校验 – 字节码校验器会校验生成的字节码是否正确如果校验失败我们会得到校验错误。
2. 准备 – 分配内存并初始化默认值给所有的静态变量。
3. 解析 – 所有符号内存引用被方法区(Method Area)的原始引用所替代。
1.3 初始化
这是类加载的最后阶段这里所有的静态变量会被赋初始值, 并且静态块将被执行。
2. 运行时数据区Runtime Data Area
The 运行时数据区域被划分为5个主要组件
2.1 方法区Method Area
所有类级别数据将被存储在这里包括静态变量。每个JVM只有一个方法区它是一个共享的资源。
2.2 堆区Heap Area
所有的对象和它们相应的实例变量以及数组将被存储在这里。每个JVM同样只有一个堆区。由于方法区和堆区的内存由多个线程共享所以存储的数据不是线程安全的。
2.3 栈区Stack Area
对每个线程会单独创建一个运行时栈。对每个函数呼叫会在栈内存生成一个栈帧(Stack Frame)。所有的局部变量将在栈内存中创建。栈区是线程安全的因为它不是一个共享资源。栈帧被分为三个子实体
a 局部变量数组 – 包含多少个与方法相关的局部变量并且相应的值将被存储在这里。
b 操作数栈 – 如果需要执行任何中间操作操作数栈作为运行时工作区去执行指令。
c 帧数据 – 方法的所有符号都保存在这里。在任意异常的情况下catch块的信息将会被保存在帧数据里面。
2.4 PC寄存器
每个线程都有一个单独的PC寄存器来保存当前执行指令的地址一旦该指令被执行pc寄存器会被更新至下条指令的地址。
2.5 本地方法栈
本地方法栈保存本地方法信息。对每一个线程将创建一个单独的本地方法栈。
3. 执行引擎
分配给运行时数据区的字节码将由执行引擎执行。执行引擎读取字节码并逐段执行。
3.1 解释器: 解释器能快速的解释字节码但执行却很慢。 解释器的缺点就是,当一个方法被调用多次每次都需要重新解释。
编译器
JIT编译器消除了解释器的缺点。执行引擎利用解释器转换字节码但如果是重复的代码则使用JIT编译器将全部字节码编译成本机代码。本机代码将直接用于重复的方法调用这提高了系统的性能。
a. 中间代码生成器 – 生成中间代码
b. 代码优化器 – 负责优化上面生成的中间代码
c. 目标代码生成器 – 负责生成机器代码或本机代码
d. 探测器(Profiler) – 一个特殊的组件负责寻找被多次调用的方法。
3.3 垃圾回收器:
收集并删除未引用的对象。可以通过调用System.gc()来触发垃圾回收但并不保证会确实进行垃圾回收。JVM的垃圾回收只收集哪些由new关键字创建的对象。所以如果不是用new创建的对象你可以使用finalize函数来执行清理。
Java本地接口 (JNI): JNI 会与本地方法库进行交互并提供执行引擎所需的本地库。
本地方法库:它是一个执行引擎所需的本地库的集合。
JVM三大核心区域 通过一个小程序认识JVM
package com.spark.jvm;
/*** 从JVM调用的角度分析java程序堆内存空间的使用* 当JVM进程启动的时候会从类加载路径中找到包含main方法的入口类HelloJVM* 找到HelloJVM会直接读取该文件中的二进制数据并且把该类的信息放到运行时的Method内存区域中。* 然后会定位到HelloJVM中的main方法的字节码中并开始执行Main方法中的指令* 此时会创建Student实例对象并且使用student来引用该对象或者说给该对象命名其内幕如下* 第一步JVM会直接到Method区域中去查找Student类的信息此时发现没有Student类就通过类加载器加载该Student类文件* 第二步在JVM的Method区域中加载并找到了Student类之后会在Heap区域中为Student实例对象分配内存* 并且在Student的实例对象中持有指向方法区域中的Student类的引用内存地址* 第三步JVM实例化完成后会在当前线程中为Stack中的reference建立实际的应用关系此时会赋值给student* 接下来就是调用方法* 在JVM中方法的调用一定是属于线程的行为也就是说方法调用本身会发生在线程的方法调用栈* 线程的方法调用栈Method Stack Frames每一个方法的调用就是方法调用栈中的一个Frame* 该Frame包含了方法的参数局部变量临时数据等 student.sayHello();*/
public class HelloJVM {//在JVM运行的时候会通过反射的方式到Method区域找到入口方法mainpublic static void main(String[] args) {//main方法也是放在Method方法区域中的/*** student(小写的)是放在主线程中的Stack区域中的* Student对象实例是放在所有线程共享的Heap区域中的*/Student student new Student(spark);/*** 首先会通过student指针或句柄指针就直接指向堆中的对象句柄表明有一个中间的,student指向句柄句柄指向对象* 找Student对象当找到该对象后会通过对象内部指向方法区域中的指针来调用具体的方法去执行任务*/student.sayHello();}
}class Student {// name本身作为成员是放在stack区域的但是name指向的String对象是放在Heap中private String name;public Student(String name) {this.name name;}//sayHello这个方法是放在方法区中的public void sayHello() {System.out.println(Hello, this is this.name);}
}JVM三大性能调优参数-Xms –Xmx –Xss
-Xms –Xmx是对堆的性能调优参数一般两个设置是一样的如果不一样当Heap不够用会发生内存抖动。一般都调大这两个参数并且两个大小一样。
-Xss是对每一个线程栈的性能调优参数,影响堆栈调用的深度
实战演示从OOM推导出JVM GC时候基于的内存结构Young GenerationEden、From、To、OldGeneration、Permanent Generation JVMHeap区域(年轻代、老年代)和方法区(永久代)结构图 从Java GC的角度解读代码程序20行new的Person对象会首先会进入年轻代的Eden中如果对象太大可能直接进入年老代。在GC之前对象是存在Eden和from中的进行GC的时候Eden中的对象被拷贝到To这样一个survive空间survive幸存空间包括from和to他们的空间大小是一样的又叫s1和s2中有一个拷贝算法From中的对象算法会考虑经过GC幸存的次数到一定次数阈值如果说每次GC之后这个对象依旧在Survive中存在GC一次他的Age就会加1默认15就会放到OldGeneration。但是实际情况比较复杂有可能没有到阈值就从Survive区域直接到Old Generation区域。在进行GC的时候会对Survive中的对象进行判断Survive空间中有一些对象Age是一样的也就是经过的GC次数一样年龄相同的这样一批对象的总和大于等于Survive空间一半的话这组对象就会进入old Generation中是一种动态的调整会被复制到OldGeneration如果没到次数From中的对象会被复制到To中复制完成后To中保存的是有效的对象Eden和From中剩下的都是无效的对象这个时候就把Eden和From中所有的对象清空。在复制的时候Eden中的对象进入To中To可能已经满了这个时候Eden中的对象就会被直接复制到Old Generation中From中的对象也会直接进入Old Generation中。就是存在这样一种情况To比较小第一次复制的时候空间就满了直接进入old Generation中。复制完成后To和From的名字会对调一下因为Eden和From都是空的对调后Eden和To都是空的下次分配就会分配到Eden。一直循环这个流程。好处使用对象最多和效率最高的就是在Young Generation中通过From to就避免过于频繁的产生FullGCOld Generation满了一般都会产生FullGC
虚拟机在进行MinorGC新生代的GC的时候会判断要进入OldGeneration区域对象的大小是否大于Old Generation剩余空间大小如果大于就会发生Full GC。
刚分配对象在Eden中如果空间不足尝试进行GC回收空间如果进行了MinorGC空间依旧不够就放入Old Generation如果OldGeneration空间还不够就OOM了。
比较大的对象数组等大于某值可配置就直接分配到老年代避免频繁内存拷贝
年轻代和年老代属于Heap空间的
Permanent Generation永久代可以理解成方法区它属于方法区也有可能发生GC例如类的实例对象全部被GC了同时它的类加载器也被GC掉了这个时候就会触发永久代中对象的GC。
如果OldGeneration满了就会产生FullGC
满原因1from survive中对象的生命周期到一定阈值
2分配的对象直接是大对象
3、由于To 空间不够进行GC直接把对象拷贝到年老代年老代GC时候采用不同的算法
如果Young Generation大小分配不合理或空间比较小这个时候导致对象很容易进入Old Generation中而Old Generation中回收具体对象的时候速度是远远低于Young Generation回收速度。
因此实际分配要考虑年老代和新生代的比例考虑Eden和survives的比例
Permanent Generation中发生GC的时候也对性能影响非常大也是Full GC
JVM GC时候核心参数
-XXNewRatio –XX:SurvivorRatio –XX:NewSize –XX:MaxNewSize
–XX:NewSize–XX:MaxNewSize指定新生代初始大小和最大大小。
1-XX:NewRatio 是年老代 新生代相对的比例比如NewRatio2表明年老代是新生代的2倍。老年代占了heap的2/3新生代占了1/3
2-XX:SurvivorRatio 配置的是在新生代里面Eden和一个Servive的比例
如果指定NewRatio还可以指定NewSizeMaxNewSize如果同时指定了会如何
NewRatio2这个时候新生代会尝试分配整个Heap大小的1/3的大小但是分配的空间不会小于-XX:NewSize也不会大于 –XX:MaxNewSize
3-XX:NewSize –XX:MaxNewSize
实际设置比例还是设置固定大小固定大小理论上速度更高。
-XX:NewSize –XX:MaxNewSize理论越大越好但是整个Heap大小是有限的一般年轻代的设置大小不要超过年老代。
-XX:SurvivorRatio新生代里面Eden和一个Servive的比例如果SurvivorRatio是5的话也就是Eden区域是SurviveTo区域的5倍。Survive由From和To构成。结果就是整个Eden占用了新生代5/7From和To分别占用了1/7,如果分配不合理Eden太大这样产生对象很顺利但是进行GC有一部分对象幸存下来拷贝到To空间小就没有足够的空间对象会被放在old Generation中。如果Survive空间大会有足够的空间容纳GC后存活的对象但是Eden区域小会被很快消耗完这就增加了GC的次数。
JVM的GC日志解读
一、 JVM YoungGeneration下MinorGC日志详解
[GC (Allocation Failure) [PSYoungGen:2336K-288K(2560K)] 8274K-6418K(9728K), 0.0112926 secs] [Times:user0.06 sys0.00, real0.01 secs]
PSYoungGen是新生代类型新生代日志收集器2336K表示使用新生代GC前占用的内存-288K表示GC后占用的内存(2560K)代表整个新生代总共大小
8274KGC前整个JVM Heap对内存的占用-6418KMinorGC后内存占用总量(9728K)整个堆的大小0.0112926 secsMinor GC消耗的时间] [Times: user0.06 sys0.00, real0.01 secs] 用户空间内核空间时间的消耗real整个的消耗
二、 JVM的GC日志Full GC日志每个字段彻底详解
[Full GC (Ergonomics) [PSYoungGen: 984K-425K(2048K)] [ParOldGen:7129K-7129K(7168K)] 8114K-7555K(9216K), [Metaspace:2613K-2613K(1056768K)], 0.1022588 secs] [Times: user0.56 sys0.02,real0.10 secs]
[Full GC (Allocation Failure) [PSYoungGen: 425K-425K(2048K)][ParOldGen: 7129K-7129K(7168K)] 7555K-7555K(9216K), [Metaspace:2613K-2613K(1056768K)], 0.1003696 secs] [Times: user0.64 sys0.03,real0.10 secs]
[Full GC表明是Full GC (Ergonomics) [PSYoungGen:FullGC会导致新生代Minor GC产生]984K-425K(2048K)][ParOldGen:老年代GC7129KGC前多大-7129KGC后并没有降低内存占用因为写的程序不断循环一直有引用(7168K) 老年代总容量] 8114KGC前占用整个Heap空间大小-7555K GC后占用整个Heap空间大小 (9216K) 整个Heap大小JVM堆的大小, [Metaspace: java6 7是permanentspacejava8改成Metaspace类相关的一些信息 2613K-2613K(1056768K) GC前后基本没变空间很大], 0.1022588 secsGC的耗时秒为单位] [Times: user0.56 sys0.02, real0.10 secs]用户空间耗时内核空间耗时真正的耗时时间
三、 Java8中的JVM的MetaSpace
Metaspace的使用C语言实现的使用的是OS的空间Native Memory Space可动态的伸缩可以根据类加载的信息的情况在进行GC的时候进行调整自身的大小来延缓下一次GC的到来。
可以设置Metaspace的大小如果超过最大大小就会OOM不设置如果把整个操作系统的内存耗尽了出现OOM一般会设置一个足够大的初始值安全其间会设置最大值。
永久代发生GC有两种情况类的所有的实例被GC掉且class load不存。
对于元数据空间 简化了GC class load不存在了就需要进行GC。
三种基本的GC算法基石
一、 标记清除算法
内存中的对象构成一棵树当有效的内存被耗尽的时候程序就会停止做两件事第一标记标记从树根可达的对象途中水红色第二清除清楚不可达的对象。标记清除的时候有停止程序运行如果不停止此时如果存在新产生的对象这个对象是树根可达的但是没有被标记标记已经完成了会清除掉。
缺点递归效率低性能低释放空间不连续容易导致内存碎片会停止整个程序运行 二、 复制算法
把内存分成两块区域空闲区域和活动区域第一还是标记标记谁是可达的对象标记之后把可达的对象复制到空闲区将空闲区变成活动区同时把以前活动区对象14清除掉变成空闲区。
速度快但耗费空间假定活动区域全部是活动对象这个时候进行交换的时候就相当于多占用了一倍空间但是没啥用。 三、 标记整理算法
平衡点
标记谁是活跃对象整理会把内存对象整理成一课树一个连续的空间 JVM垃圾回收分代收集算法
综合了上述算法优略
1 分代GC在新生代的算法采用了GC的复制算法速度快因为新生代一般是新对象都是瞬态的用了可能很快被释放的对象。
2 分代GC在年老代的算法 标记整理算法GC后会执行压缩整理到一个连续的空间这样就维护着下一次分配对象的指针下一次对象分配就可以采用碰撞指针技术将新对象分配在第一个空闲的区域。
JVM垃圾回收器串行、并行、并发垃圾回收器概述
1 JVM中不同的垃圾回收器
2 串行并行并发垃圾回收器和JVM历史有关系刚开始串行
Java中Stop-The-World机制简称STW是在执行垃圾收集算法时Java应用程序的其他所有线程都被挂起除了垃圾收集帮助器之外。Java中一种全局暂停现象全局停顿所有Java代码停止native代码可以执行但不能与JVM交互这些现象多半是由于gc引起。
JVM中Serial收集器、ParNew收集器、Parallel收集器解析
Serial收集器 单线程方式没有线程切换开销如果受限物理机器单线程可采用串行且采用stop the world在工作的时候程序会停止
Serial和serial old
ParNew收集器多线程多CPU和多Core的环境中高效生产环境对低延时要求高的话就采用ParNew和CMS组合来进行server端的垃圾回收
Parallel 收集器多线程并行 它可以控制JVM吞吐量的大小吞吐量优先的收集器一般设置1%可设置程序暂停的时间会通过把新生代空间变小来完成回收频繁的小规模垃圾回收会影响程序吞吐量大小
JVM中CMS收集器解密
低延迟进行垃圾回收在线服务和处理速度要求高的情况下很重要
配置XX:UseConcMarkSweepGC
concurrence并发 Mark标记Sweep清理
低延时
把垃圾回收分成四个阶段
CMS-initial-mark初始标记阶段会stop the world短暂的暂停程序根据跟对象标记的对象所连接的对象是否可达来标记出哪些可到达
CMS-concurrent-mark并发标记根据上一次标记的结果确定哪些不可到达线程并发或交替之行基本不会出现程序暂停。
CMS-remark再次标记会出现程序暂停所有内存那一时刻静止确保被全部标记有可能第二阶段之前有可能被标记为垃圾的对象有可能被引用在此标记确认。
CMS-concurrent-sweep并发清理垃圾把标记的垃圾清理掉了没有压缩有可能产生内存碎片不连续的内存块这时候就不能更好的使用内存可以通过一个参数配置根据内存的情况执行压缩。
JVM中G1收集器
可以像CMS收集器一样GC操作与应用的现场一起并发执行
紧凑的空闲内存区域且没有很长的GC停顿时间
需要可预测的GC暂停耗时
不想牺牲太多吞吐量性能
启动后不需要请求更大的Java堆
通过案例瞬间理解JVM中PSYoungGen、ParOldGen、MetaSpace
HeapPSYoungGen total 2560K, used 321K[0x00000007bfd00000, 0x00000007c0000000, 0x00000007c0000000)eden space 2048K, 15% used[0x00000007bfd00000,0x00000007bfd50568,0x00000007bff00000)from space 512K, 0% used[0x00000007bff00000,0x00000007bff00000,0x00000007bff80000)to space 512K, 0% used[0x00000007bff80000,0x00000007bff80000,0x00000007c0000000)ParOldGen total 7168K, used 7097K[0x00000007bf600000, 0x00000007bfd00000, 0x00000007bfd00000)object space 7168K, 99%used [0x00000007bf600000,0x00000007bfcee7b8,0x00000007bfd00000)Metaspace used 2647K, capacity 4486K, committed4864K, reserved 1056768Kclass space used 289K, capacity 386K, committed 512K,reserved 1048576KPSYoungGen是eden from
使用MAT对Dump文件进行分析实战
导出Dump文件 MapReduce过程详解及其性能优化
转发:https://blog.csdn.net/aijiudu/article/details/72991993