当前位置: 首页 > 后端技术 > Java

爆整理JVM十大模块知识点,不信你还是不懂

时间:2023-04-01 22:25:14 Java

01JVM内存结构Java虚拟机的内存空间分为5个部分:与JDK1.7相比,最大的区别是元数据区取代了永久一代。元空间的本质类似于永久代,是JVM规范中方法区的实现。但是,元空间和永久代最大的区别在于,元空间不在虚拟机中,而是使用本地内存。1.1程序计数器(PC寄存器)(1)程序计数器的定义程序计数器是一块很小的内存空间,它是当前线程正在执行的字节码指令的地址。如果当前线程正在执行na??tive方法,那么此时程序计数器是Undefined。(2)程序计数器的作用字节码解释器通过改变程序计数器来顺序读取指令,从而实现代码的流程控制。在多线程的情况下,程序计数器会记录当前线程的执行位置,这样当线程切换回来的时候,就知道上次线程执行到哪里了。(3)程序计数器的特点?内存空间小。线程私有,每个线程都有自己的程序计数器。生命周期:随着线程的创建而创建,随着线程的结束而销毁。是唯一不会发生OutOfMemoryError的内存区域。由于文章篇幅,部分内容将以图片展示。如果有需要查看完整文档的小伙伴,关注一下,点赞+关注【点这里】获取!!1.2Java虚拟机栈(Javastack)(1)Java虚拟机栈的定义Java虚拟机栈是一种描述Java方法运行过程的内存模型。Java虚拟机栈会为每个即将运行的Java方法创建一个称为“栈帧”的区域,用于存储方法运行过程中的一些信息,如:局部变量表操作数栈动态链接方法exitinformation......(2)push和pop过程当方法运行过程中需要创建局部变量时,将局部变量的值存储在栈帧中的局部变量表中。Java虚拟机栈顶的栈帧是当前正在执行的活跃栈,即当前正在执行的方法,PC寄存器也会指向这个地址。操作数栈只能使用这个活动栈帧的局部变量。当在这个栈帧中调用另一个方法时,会再次创建对应的栈帧,并将新创建的栈帧压入栈顶,成为当前活跃的栈帧。方法结束后,当前栈帧被移除,栈帧的返回值成为新活动栈帧中操作数栈的操作数。如果没有返回值,则新活动堆栈帧中操作数堆栈上的操作数不变。由于Java虚拟机栈对应线程,数据不被线程共享,所以不需要关心数据一致性问题,也不会有同步锁问题。(3)Java虚拟机栈的特点局部变量表是随着栈帧的创建而创建的,其大小在编译时就确定了,只需要在创建时分配一个预定的大小即可。局部变量表的大小在方法运行时不会改变。Java虚拟机栈中会出现两个异常:StackOverFlowError和OutOfMemoryError。StackOverFlowError如果Java虚拟机栈的大小不允许动态扩展,那么当线程请求栈的深度超过当前Java虚拟机栈的最大深度时,抛出StackOverFlowError异常。02HotSpot虚拟机对象探索2.1对象内存布局在HotSpot虚拟机中,对象内存布局分为以下三个区域:对象头(Header)、实例数据(InstanceData)对齐填充(Padding)(1)对象头objectheader记录了对象运行时需要用到的一些数据:hashcodeGCgenerationalagelockstatusflag线程持有的锁biasedthreadIDbiasedtimestamp对象头可能包含一个类型指针,通过它可以确定对象是哪个班级。如果对象是数组,则对象头还包括数组长度。(2)实例数据实例数据部分是成员变量的值,包括父类的成员变量和本类的成员变量。(3)Alignmentpadding用于保证对象的总长度为8字节的整数倍。HotSpotVM的自动内存管理系统要求对象大小必须是8字节的整数倍。而objectheader部分正好是8字节的倍数(1倍或2倍),因此,当objectinstancedata部分不对齐时,需要通过alignmentpadding来完成。Alignmentpadding不一定存在,也没有什么特殊意义,它只是作为一个占位符,在方法开始时将帧压入栈中,在方法结束时弹出栈。这些区域的内存分配和回收是确定性的,不需要过多考虑这些区域的回收,因为当方法结束或者线程结束时,内存自然会被回收。对于Java堆和方法区,我们只能知道在程序运行过程中会创建哪些对象。这部分内存的分配和回收是动态的,垃圾收集器关注的正是这部分内存。3.1判断对象是否存活如果一个对象没有被任何对象或变量引用,那么它就是一个无效对象,需要被回收。(1)引用计数方法在对象头维护一个计数器计数器,对象被引用一次时计数器+1;如果引用无效,则计数器为-1。当计数器达到0时,该对象被视为无效。引用计数算法实现简单,判断效率也很高。在大多数情况下,这是一个很好的算法。但是主流的Java虚拟机并没有使用引用计数算法来管理内存,主要是因为很难解决对象之间的循环引用问题。比如栗子对象objA和objB都有字段instance,这样objA.instance=objB和objB.instance=objA,因为它们相互引用,所以它们的引用计数不为0,所以引用计数算法无法通知GC收集机器回收它们。(2)可达性分析方法所有与GCRoots直接或间接关联的对象都是有效对象,不与GCRoots关联的对象都是无效对象。GCRoots是指:Java虚拟机栈中引用的对象(栈帧中的局部变量表)本地方法栈中引用的对象方法区常量引用的对象方法区类静态属性引用的对象GCRoots做的不包括堆中对象引用的对象,这样就不会有循环引用的问题。04HotSpotGarbageCollectorHotSpot虚拟机提供了多种垃圾收集器,每一种都有自己的特点。虽然我们要比较各种收藏家,但并不是要挑出最好的。我们只为特定应用选择最合适的收集器。4.1新一代垃圾收集器(1)串行垃圾收集器(单线程)只启动一个GC线程进行垃圾收集,垃圾收集时停止所有用户线程(StopTheWorld)。通常,客户端应用程序需要的内存较少,不会创建过多的对象,并且堆内存较小,因此垃圾收集器的回收时间较短。即使在此期间停止所有用户线程,也不会有明显的滞后。所以串行垃圾收集器适合客户端使用。由于Serial收集器只使用一个GC线程,避免了线程切换的开销,简单高效。(2)ParNew垃圾收集器(多线程)ParNew是Serial的多线程版本。垃圾收集由多个GC线程并行执行。但是清理过程仍然需要StopTheWorld。ParNew追求“低停顿时间”。与Serial的唯一区别是它使用多线程进行垃圾收集。在多CP??U环境下,性能相比Serial会有一定程度的提升;但是线程切换需要额外的开销,所以在单CPU环境下性能不如Serial。05内存分配与回收策略06JVM性能调优目前在高性能硬件上部署程序主要有两种方式:使用64位JDK使用大内存;使用多个32位虚拟机建立逻辑集群,以利用硬件资源。07类文件结构08类加载时机09类加载过程10类加载器10.1类与类加载器(1)判断一个类是否“相等”任何一个类都是由类加载器和类本身来加载的以确立其在Java中的唯一性虚拟机中,每个类加载器都有一个独立的类命名空间。因此,比较两个类是否“相等”只有在两个类由同一个类加载器加载时才有意义,否则,即使两个类源自同一个Class文件,由同一个虚拟机加载加载,只要由于加载它们的类加载器不同,因此这两个类必须不相等。这里的“相等”包括代表类的Class对象的equals()方法,isInstance()方法的返回结果,以及使用instanceof关键字来判断对象的关系。