| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> Java知识库 -> JavaEE核心【JVM 的作用、类加载器、JVM内存模型、GC回收机制】 -> 正文阅读 |
|
[Java知识库]JavaEE核心【JVM 的作用、类加载器、JVM内存模型、GC回收机制】 |
1、JVM的作用 ????????JVM是Java Virtual Machine的缩写。我们安装的JDK中包含了JRE,在JRE中,包含了java的虚拟机和核心类库,如果想要运行java程序,则需要上述的JRE环境。 ????????java是一门高级程序语言,直接运行在硬件上并不现实,所以要在运行之前,需要对其进行一些转换。 转换过程:通过编译器将java程序转换成虚拟机能识别的指令序列,也叫做java字节码。java虚拟机会将字节码文件(.class文件)加载到JVM中,由JVM进行解释和执行。 ????????JVM运行在操作系统之上,与硬件没有直接的交互。 ?2、类加载器 ? ? ? ? 类加载器(ClassLoad),负责加载class文件,经过类加载器加载并初始化之后,会得到Class,实例化对象的时候会参考Class。如Person.class经过ClassLoad加载初始化后,会得到Person Class,之后每次实例化对象的时候,都会参考Person Class。 ? ? ? ? 类加载器的分类 ? ? ? ??启动类加载器(Bootstrap):主要负责加载jre中的最为基础、最为重要的类。如$JAVA_HOME/jre/lib/rt.jar等。它由C++代码实现,没有对应的java对象,因此在java中,尝试获取此类时,只能使用null来指代。 ? ? ? ? 扩展类加载器(Extension):由Java代码实现,用于加载相对次要、但又通用的类,比如存放在?JRE 的 lib/ext 目录下 jar 包中的类。 ? ? ? ? 应用程序类加载器(AppClassLoad):由Java代码实现, 它负责加载应用程序路径下的类。比如Person.java,Car.java等程序员自定义的类。 ? ? ? ? 用户自定义加载器。 ? ? ? ? 其中Bootstrap是Extension的父类加载器,Extension是AppClassLoad的父类加载器。 ? ? ? ? 双亲委派机制 ????????每当一个类加载器接收到加载请求时,它会先将请求转发给父类加载器(在此加载器中如果还有父类加载器,依旧向上请求)。在父类加载器没有找到所请求的类的情况下,该类加载器才会向下尝试去加载。在AppClassLoad中仍然没有找到所请求的类,会抛出ClassNotFound异常。当子类和父类同时有相同的类时,由双亲委派机制决定,会优先使用父类的。 ????????好处: ????????通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次 ? ? ? ? ?防止java核心api中定义类型不会被用户恶意替换和篡改,从而引发错误。 3、JVM内存模型 ????????(1)Execution Engine:执行引擎(Execution Engine)负责解释命令,提交操作系统执行。 ????????????????当JVM需要调用系统的硬件时,如CPU,硬盘等,需要向操作系统发送命令,但命令操作系统无法理解,这时就需要Execution Engine负责将命令解释并提交给操作系统。 ? ? ? ? (2)Native Method Stack:本地方法栈(?Native Method Stack)在每个操作系统内部,都定义了很多本地方法库。这些本地方法库中,定义了很多调用本地操作系统的方法,也称之为本地方法接口。 ????????????????当需要执行Native方法时,需要将Native方法压入Native Method Stack,然后向操作系统发送指令,交给执行引擎(Execution Engine) 解释命令,之后调用本地方法接口(Native Interface),调用本地方法接口时又会使用到本地方法库。 ? ? ? ??(3)Program Counter Register:程序计数器,也叫PC寄存器,是一个小指针,提示下一个需要执行栈内的哪一个方法。 ? ? ? ? (4)Method Area:方法区(Method Area),方法区是被所有线程共享,所有定义的方法的信息都保存在该区域,此区属于共享区间。 ????????????????静态变量 + 常量 + 类信息(构造方法/接口定义) + 运行时常量池存在方法区中。 ? ? ? ? (5)Stark:栈,栈也叫栈内存,主管Java程序的运行,是在线程创建时创建,它的生命期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束该栈就结束,生命周期和线程一致,是线程私有的。 ????????????????8种基本类型的变量(byte,short,int,double,float,long,char,bool)+ 对象的引用变量 + 实例方法都是在栈内存中分配。 ????????????????在栈区域规定了两种异常状态:如果线程请求的栈深度大于虚拟机所允许的深度,则抛出StackOverflowError异常;如果虚拟机栈可以动态扩展,在扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。 ? ? ? ????????? 栈帧:一个线程的每个方法在调用时都会在栈上划分一块区域,用于存储方法所需要的变量等信息,这块区域称之为栈帧(stack frame)。栈由多个栈帧构成,好比一部电影由多个帧的画面构成。 ? ? ? ? ? ? ? ? 栈运行原理:栈中的数据都是以栈帧(Stack Frame)为载体存在。在栈中,方法的调用顺序遵循“先进后出”/“后进先出”原则。 ? ? ? ? (6)堆heap ????????堆的逻辑设计:堆是java虚拟机所管理的内存中最大的一块,是被所有线程共享的一块内存区域,在虚拟机启动时创建。堆内存的大小是可以调节的(通过?-Xmx 和 -Xms 控制),一般为物理内存的1 / 64 ,最大为物理内存的1 / 4。在逻辑上可以划分为三部分,新生区(Young Generation Space)、养老区(Tenure generation space)、永久区(Permanent Space),永久区在JDK1.8后为元空间。 ????????堆的物理设计:在?Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。这样划分的目的是为了使?JVM 能够更好的管理堆内存中的对象,包括内存的分配以及回收。? ?????????????????新生代分为eden区、from和to区,他们是两块大小相等并且可以互换角色的空间(当进行轻量级垃圾回收后仍然存在对象的区称为from区)。绝大多数情况下,新new出来的对象首先分配在eden区,在新生代回收后,如果对象还存活(被引用),则进入from或to区,之后每经过一次新生代回收,如果对象存活则它的年龄就加1,对象达到一定的年龄(默认15)后,则进入老年代,否则进入to区。在新生代进行垃圾回收使用的是轻量级垃圾回收(Minor GC),在老年代中进行的是重量级的垃圾回收(Major GC),当在老年代中无法进行垃圾回收,会触发OOM(OutOfMemory)。 ????????永久区perm:永久存储区是一个常驻内存区域,用于存放JDK自身所携带的 Class,Interface 的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会轻易被垃圾回收器回收掉的,关闭 JVM 才会释放此区域所占用的内存。 ????????Jdk1.6及之前: 有永久代, 字符串常量池1.6在方法区 ????????Jdk1.7: ???????????有永久代,但已经逐步“去永久代”,字符串常量池1.7在堆 ????????Jdk1.8及之后: 无永久代,用元空间替代 4、GC ????????JVM中的Garbage Collection,简称GC,它会不定时去堆内存中清理不可达(没有被引用)对象。 ? ? ? ? GC分类:???????????????? ????????????????JVM在进行GC时,并非每次都对上面三个内存区域一起回收的,大部分时候回收的都是指新生代。因此GC按照回收的区域又分了两种类型,一种是普通GC(minor GC),一种是全局GC(major GC or Full GC), ????????新生代GC(minor GC):只针对新生代区域的GC。 ??????????????老年代GC(major GC or Full GC):针对老年代的GC,偶尔伴随对新生代的GC以及对永久代的GC。 ?????????????Minor GC触发机制:当年轻代满时就会触发Minor GC,这里的年轻代满指的是Eden区满,Survivor满不会引发GC。 ? ? ? ? ? ? ? ?Full GC触发机制:当年老代满时会引发Full GC,Full GC将会同时回收年轻代、年老代,当永久代满时也会引发Full GC,会导致Class、Method元信息的卸载。 ? ? ? ? GC工作特点:理论上GC过程中会频繁收集Young区,很少收集Old区,基本不动Perm区(元空间/方法区)。 ? ? ? ? 标记不可达对象的引用计数法:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值加1;当引用失效时,计数器值减1.任何时刻计数器值为0的对象就是不可能再被使用的。引用计数法就是如果一个对象没有被任何引用指向,则可视之为垃圾。这种方法的缺点就是不能检测到循环指向的存在。主流的Java虚拟机里面都没有选用引用计数算法来管理内存。 ????????标记不可达对象的可达性分析(GC Roots算法):根搜索算法的基本思路就是通过一系列名为”GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。 ? ? ? ? 垃圾回收的三种方式:? ? ? ? ? ? ? ? ? 清除:把死亡对象所占据的内存标记为空闲内存,并记录在一个空闲列表(free list)之中。当需要新建对象时,内存管理模块便会从该空闲列表中寻找空闲内存,并划分给新建的对象。清除这种回收方式的原理及其简单,但是有两个缺点。一是会造成内存碎片。由于?Java 虚拟机的堆中对象必须是连续分布的,因此可能出现总空闲内存足够,但是无法分配的极端情况。另一个则是分配效率较低。如果是一块连续的内存空间,那么我们可以通过指针加法(pointer bumping)来做分配。而对于空闲列表,Java 虚拟机则需要逐个访问列表中的项,来查找能够放入新建对象的空闲内存。 ? ? ? ? ? ? ? ? 压缩:把存活的对象聚集到内存区域的起始位置,从而留下一段连续的内存空间。这种做法能够解决内存碎片化的问题,但代价是压缩算法的性能开销。 ? ? ? ? ? ? ? ? 复制:把内存区域分为两等分,分别用两个指针 from 和 to 来维护,并且只是用 from 指针指向的内存区域来分配内存。当发生垃圾回收时,便把存活的对象复制到 to 指针指向的内存区域中,并且交换 from 指针和 to 指针的内容。复制这种回收方式同样能够解决内存碎片化的问题,但是它的缺点也极其明显,即堆空间的使用效率极其低下。复制必交换,谁空谁为to。 ? ? ? ? ? ? ? ? 总结:回收死亡对象的内存共有三种方式,分别会造成内存碎片的清除、性能开销较大的压缩、以及堆使用效率较低的复制。当然,现代的垃圾回收器往往会综合上述几种回收方式,综合它们优点的同时规避它们的缺点。 ? ? ? ? 垃圾回收四大算法: ????????????????标记复制(Mark-Copying)算法: ????????????????当我们调用?new 指令时,它会在 Eden 区中划出一块作为存储对象的内存。当?Eden 区的空间耗尽了,?Java 虚拟机便会触发一次 Minor GC,来收集新生代的垃圾。存活下来的对象,则会被送到 Survivor 区。 ????????????????新生代共有两个?Survivor 区,我们分别用 from 和 to 来指代。其中 to 指向的 Survivior 区是空的。当发生?Minor GC 时,Eden 区和 from 指向的 Survivor 区中的存活对象会被复制到 to 指向的 Survivor 区中,然后交换 from 和 to 指针,以保证下一次 Minor GC 时,to 指向的 Survivor 区还是空的。 ????????????????Java 虚拟机会记录 Survivor 区中的对象一共被来回复制了几次。如果一个对象被复制的次数为 15(对应虚拟机参数 -XX:+MaxTenuringThreshold),那么该对象将被晋升(promote)至老年代。另外,如果单个 Survivor 区已经被占用了 50%(对应虚拟机参数 -XX:TargetSurvivorRatio),那么较高复制次数的对象也会被晋升至老年代。 ????????????????万一存活对象数量比较多,那么To域的内存可能不够存放,这个时候会借助老年代的空间。 ????????????????因此Minor GC使用的则是标记-复制算法。将?Survivor 区中的老存活对象晋升到老年代,然后将剩下的存活对象和 Eden 区的存活对象复制到另一个 Survivor 区中。理想情况下,Eden 区中的对象基本都死亡了,那么需要复制的数据将非常少,因此采用这种标记 - 复制算法的效果极好。 ????????????????标记清除(Mark-Sweep)算法:???????????????? ????????????????老年代一般是由标记清除或者是标记清除与标记整理的混合实现。标记清除算法一般应用于老年代,因为老年代的对象生命周期比较长。该算法先对所有可访问的对象,做个标记再遍历堆,把未被标记的对象回收(标记活的)。???????????????? ????????????????缺点:回收时,应用需要挂起,也就是stop the world,导致用户体验极差。由于需要遍历全堆对象,效率比较低(递归与全堆对象遍历)。造成内存碎片化。 ????????????????标记压缩(Mark--Compact)算法: ????????????????标记清除算法和标记压缩算法非常相同,但是标记压缩算法在标记清除算法之上解决内存碎片化,也消除了复制算法当中,内存减半的高额代价。但效率低,压缩阶段,由于移动了可用对象,需要去更新引用。 ????????????????标记清除压缩(Mark-Sweep-Compact)算法: ????????????????标记清除压缩(Mark-Sweep-Compact)算法是标记清除算法和标记压缩算法的结合算法。其原理和标记清除算法一致,只不过会在多次GC后,进行一次Compact操作! |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/24 12:04:58- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |