当前位置: 首页 > news >正文

网站推广由什么样的人来做戒赌网站怎么做

网站推广由什么样的人来做,戒赌网站怎么做,wordpress 访客记录,应用下载app面试常见#xff1a; 请你谈谈你对JVM的理解?java8虚拟机和之前的变化更新?什么是OOM#xff0c;什么是栈溢出StackOverFlowError? 怎么分析?JVM的常用调优参数有哪些?内存快照如何抓取#xff1f;怎么分析Dump文件#xff1f;谈谈JVM中#xff0c;类加载器你的认识…面试常见 请你谈谈你对JVM的理解?java8虚拟机和之前的变化更新?什么是OOM什么是栈溢出StackOverFlowError? 怎么分析?JVM的常用调优参数有哪些?内存快照如何抓取怎么分析Dump文件谈谈JVM中类加载器你的认识 请你谈谈你对JVM的理解? JVMJava虚拟机是Java程序的运行环境它允许Java程序在不同的平台上运行。JVM负责将Java源代码编译成字节码并在运行时解释或编译执行这些字节码。JVM还负责内存管理、垃圾回收、安全性等任务。它的主要优点是跨平台性和自动内存管理。 java8虚拟机和之前的变化更新? Java 8的虚拟机相比之前的版本有一些变化和更新其中包括 Lambda表达式和函数式接口的支持使得在JVM中能更方便地使用函数式编程风格。元空间Metaspace的引入取代了永久代Permanent Generation用于存储类的元数据信息。默认方法Default Methods的支持允许接口中有具体的方法实现。增强的垃圾回收器性能和功能。新的时间和日期APIjava.time包的引入。 什么是OOM什么是栈溢出StackOverFlowError? 怎么分析? OOMOut of Memory是指JVM内存不足无法分配更多的内存给应用程序使用导致应用程序无法继续执行的情况。这可能是由于堆内存溢出、方法区元空间溢出或者线程栈空间不足等原因引起的。栈溢出StackOverflowError是指线程调用栈的深度超过了JVM所允许的最大深度导致栈空间耗尽而抛出的错误。通常是由于递归调用或者方法调用层级过深引起的。要分析这些问题可以通过查看JVM的日志、堆栈跟踪信息以及内存使用情况来确定问题的根源并可能通过调整JVM参数或者修改代码来解决问题。 JVM的常用调优参数有哪些? 常用的JVM调优参数包括 -Xms设置初始堆大小-Xmx设置最大堆大小-Xss设置线程栈大小-XX:PermSize和-XX:MaxPermSize设置永久代Java 8之前或元空间Java 8及之后大小-XX:UseG1GC启用G1垃圾回收器-XX:UseConcMarkSweepGC启用CMS垃圾回收器-XX:MaxGCPauseMillis设置最大垃圾回收停顿时间-XX:NewRatio设置新生代与老年代的大小比例 内存快照如何抓取怎么分析Dump文件 内存快照可以通过JVM工具如jmap、jcmd或者第三方工具如VisualVM、MAT来抓取。分析Dump文件通常可以使用MATMemory Analyzer Tool等工具这些工具能够解析Dump文件并生成分析报告帮助定位内存泄漏、对象占用过多内存等问题。 谈谈JVM中类加载器你的认识 类加载器负责将类文件加载到JVM中并生成对应的Class对象。JVM中的类加载器主要分为三种启动类加载器Bootstrap ClassLoader、扩展类加载器Extension ClassLoader和应用程序类加载器Application ClassLoader。类加载器采用双亲委派模型即当一个类加载器收到加载类的请求时它会先将请求委派给父类加载器处理只有在父类加载器无法完成加载时才会自己尝试加载。这种机制保证了类的唯一性和安全性。自定义类加载器可以实现一些特殊的类加载行为例如从网络或者其他非标准的地方加载类。 1.JVM的位置 JVMJava虚拟机, Java Virtual Machine是Java程序的运行环境它负责解释Java字节码并将其转换为机器码以在特定平台上执行。JVM通常安装在操作系统之上并提供了一个虚拟的运行时环境来执行Java应用程序。 JVM的位置通常取决于安装的Java开发工具包JDK。在安装了JDK的系统中JVM的二进制文件通常存储在JDK安装目录的子目录中例如在Windows系统中它可能位于C:\Program Files\Java\jdkversion\bin目录下。 三种JVM: Sun公司HotSpot 用的最多BEAJRockitIBMJ9VM 我们学习都是HotSpot 2.JVM的体系结构 红色的为多个线程共享,灰色的为单个线程私有的 线程间共享:堆,方法区. 线程私有:程序计数器,栈,本地方法栈. 程序在执行之前先要把 java 代码转换成字节码class 文件jvm 首先需要把字节码通过一定的方式 类加载器ClassLoader 把文件加载到内存中的运行时数据区Runtime Data Area的方法区Method Area 而字节码文件是 jvm 的一套指令集规范并不能直接交个底层操作系统去执行因此需要特定的命令解析器 执行引擎Execution Engine 将字节码翻译成底层系统指令再交由CPU 去执行而这个过程中需要调用其他语言的接口 本地库接口Native Interface 来实现整个程序的功能这就是这 4 个主要组成部分的职责与功能。 而我们通常所说的 JVM 组成指的是 运行时数据区Runtime Data Area 因为通常需要程序员调试分析的区域就是“运行时数据区”或者更具体的来说就是“运行时数据区”里面的 Heap堆模块。 jvm调优99%都是在方法区和堆大部分时间调堆。 JNIjava native interface本地方法接口。 为什么栈和 P C 没有垃圾回收 \red {为什么栈和PC没有垃圾回收} 为什么栈和PC没有垃圾回收 栈Stack和 PCProgram Counter程序计数器通常不需要进行垃圾回收因为它们所占用的内存空间具有确定的生命周期并且由编译器或虚拟机进行管理。 栈 栈是一种数据结构用于存储函数调用时的局部变量、函数参数、返回地址等数据。栈上的数据存储遵循后进先出LIFO的原则每当函数调用时都会为该函数分配一块栈帧Stack Frame用于存储相关数据。当函数执行完毕时其对应的栈帧会被弹出从而释放其占用的内存空间。这种自动的分配和释放机制使得栈上的内存管理成本较低并且不需要进行垃圾回收。 程序计数器 程序计数器是一种特殊的寄存器用于存储当前线程正在执行的指令地址。在程序执行过程中程序计数器会随着指令的执行而不断更新指向下一条将要执行的指令。程序计数器的生命周期与线程的生命周期密切相关当线程结束时程序计数器的内容也会被销毁。由于程序计数器的内容是由硬件直接管理的因此不需要进行垃圾回收。 3.类加载器 作用加载Class文件——如果new Student();具体实例在堆里引用变量名放栈里 。先来看看一个类加载到 JVM 的一个基本结构 类是模板对象是具体的通过new来实例化对象。car1car2car3名字在栈里面真正的实例具体的数据在堆里面栈只是引用地址。 类加载器分类 JVM角度 引导类加载器(启动类加载器 Bootstrap ClassLoader). 其他所有类加载器,这些类加载器由 java 语言实现,独立存在于虚拟机外部,并 且全部继承自抽象类 java.lang.ClassLoader. 开发人员角度 启动类加载器/根加载器/启动类加载器 Bootstrap ClassLoader 加载 JAVA_HOME\jre\lib\rt.jar 中的类(runtime, Java 运行环境的核心库目录) 启动类加载器通常只加载指定的核心类库rt.jar并不会加载 lib 目录下的其他 JAR 文件。 这个类加载器使用 C/C语言实现,嵌套在 JVM 内部.它用来加载 java 核心类库.并不继承于 java.lang.ClassLoader 没有父加载器java 程序获取不到 负责加载扩展类加载器和应用类加载器,并为他们指定父类加载器. 出于安全考虑,引用类加载器只加载存放在JAVA_HOME\jre\lib\rt.jar 目录,或者被-Xbootclasspath 参数锁指定的路径中存储放的类 扩展类加载器 Extension ClassLoader Java 语言编写的,由 sun.misc.Launcher$ExtClassLoader 实现. 派生于 ClassLoader 类. 系统类加载器/应用程序类加载器 System/App ClassLoader 从 java.ext.dirs 系统属性所指定的目录中加载类库,或从 JDK 系统安装目录的JAVA_HOME\jre\lib\ext 子目录(扩展目录)下加载类库.如果用户创建的 jar 放在此目录下,也会自动由扩展类加载器加载 Java 语言编写的,由 sun.misc.Launcher$AppClassLoader 实现. 派生于 ClassLoader 类. 加载我们自己定义的类,用于加载用户类路径(classpath)上所有的类. 该类加载器是程序中默认的类加载器. ClassLoader 类 它 是 一 个 抽 象 类 其 后 所 有 的 类 加 载 器 都 继 承 自 ClassLoader不包括启动类加载器 package github.JVM.Demo01;/** * author subeiLY * create 2021-06-08 07:42 */ public class Test01 {public static void main(String[] args) {Test01 test01 new Test01();Test01 test02 new Test01();Test01 test03 new Test01();System.out.println(test01.hashCode());System.out.println(test02.hashCode());System.out.println(test03.hashCode()); /*18360192403250408041173230247 */Class? extends Test01 aClass1 test01.getClass();ClassLoader classLoader aClass1.getClassLoader();System.out.println(classLoader);System.out.println(classLoader.getParent());System.out.println(classLoader.getParent().getParent()); /*sun.misc.Launcher$AppClassLoader18b4aac2sun.misc.Launcher$ExtClassLoader330bedb4null */Class? extends Test01 aClass2 test02.getClass();Class? extends Test01 aClass3 test03.getClass();System.out.println(aClass1.hashCode());System.out.println(aClass2.hashCode());System.out.println(aClass3.hashCode());/* 2133927002 2133927002 2133927002 */} }类加载器的分类 Bootstrap ClassLoader 启动类加载器Extention ClassLoader 标准扩展类加载器Application ClassLoader 应用类加载器User ClassLoader 用户自定义类加载器 类加载过程 2、2、1 加载 通过类名地址获取此类的二进制字节流。将这个字节流所代表的静态存储结构转换为方法区元空间的运行时结构。在内存中生成一个代表这个类的 java.lang.Class 对象作为这个类的各种数据的访问入口。 2、2、2 链接 验证 文件格式验证检查 class 文件是否以 CA FE BA BE 开头。主、次版本号验证确认版本号在当前 Java 虚拟机接收范围内。对于 Java 17.0.1其主版本号是 17次版本号是 0, 1 表示补丁或更新版本。元数据验证对字节码描述的信息进行语义分析确保符合 Java 语言规范的要求。 准备 为类的静态属性分配内存并设置默认初始值。不包含用 final 修饰的 static 常量在编译时进行初始化。 解析 将类的二进制数据中的符号引用替换成直接引用。(符号引用是 Class 文件的逻辑符号直接引用指向的方法区中某一个地址) 2、2、3 初始化 为类的静态变量赋予正确的初始值执行类构造器方法 clinit() 的过程。该方法是编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并而来的。 实例化过程 类加载完成 以下过程 创建对象一旦类加载和初始化完成就可以创建该类的对象实例了。对象实例化的过程包括 分配内存空间在堆内存中为对象分配内存空间内存大小取决于对象的大小和结构。 初始化对象将对象的实例变量即非静态变量设置为默认值如果有显式的初始化操作则会执行对应的初始化代码。 调用构造方法在内存中分配了足够的空间后会调用对象的构造方法进行初始化。构造方法负责对对象进行初始化操作可以设置对象的初始状态和执行其他必要的操作。 返回引用对象实例化完成后会返回对象的引用以便能够在程序中使用该对象。 类什么时候初始化 每个类或接口被首次主动使用时才对其进行初始化主动使用包括 通过 new 关键字创建对象。访问类的静态变量包括读取和更新。访问类的静态方法。对某个类进行反射操作。初始化子类会导致父类的初始化。执行该类的 main 函数。 除了以上几种主动使用以下情况被动使用不会加载类 引用该类的静态常量但注意只有已经指定字面量的常量不会导致初始化对于需要计算才能得出结果的常量会导致类加载。 //不会导致类初始化,被动使用 public final static int NUMBER 5 ; //会导致类加载 public final static int RANDOM new Random().nextInt() ;构造某个类的数组时不会导致该类的初始化。 Student[] students new Student[10] ;4.双亲委派机制 package java.lang;/** * author subeiLY * create 2021-06-08 08:06 */ public class String {/* 双亲委派机制:安全 1.APP--EXC--BOOT(最终执行) BOOT EXC APP */public String toString() {return Hello;}public static void main(String[] args) {String s new String();System.out.println(s.getClass());s.toString();}/* 1.类加载器收到类加载的请求 2.将这个请求向上委托给父类加载器去完成一直向上委托知道启动类加载 3.启动加载器检查是否能够加载当前这个类能加载就结束使用当前的加载器否则抛出异常适知子加载器进行加载 4.重复步骤3 */ }idea报了一个错误 双亲委派机制 如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请 求委托给父类的加载器去执行. 如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终 将到达顶层的启动类加载器. 如果父类加载器可以完成类的加载任务,就成功返回,倘若父类加载器无法完 成加载任务,子加载器才会尝试自己去加载,这就是双亲委派机制. 如果均加载失败就会抛出 ClassNotFoundException 异常。 优点 安全可避免用户自己编写的类替换 Java 的核心类如 java.lang.String. 避免类重复加载当父亲已经加载了该类时就没有必要子 ClassLoader 再加载一次 如何打破双亲委派机制 Java 虚拟机的类加载器本身可以满足加载的要求但是也允许开发者自定义类加载器。 在 ClassLoader 类中涉及类加载的方法有两个,loadClass(String name), findClass(String name),这两个方法并没有被 final 修饰,也就表示其他子类可以重写. 重写 loadClass 方法(是实现双亲委派逻辑的地方,修改他会破坏双亲委派机制, 不推荐) 重写 findClass 方法 (推荐) 我们可以通过自定义类加载重写方法打破双亲委派机制, 再例如 tomcat 等都有自己定义的类加载器. 关于双亲委派机制的博客 你确定你真的理解“双亲委派“了吗 双亲委派模型中类加载器之间的父子关系一般不会以继承Inheritance的关系来实现而是都使用组合Composition关系来复用父加载器的代码的。 实现双亲委派的代码都集中在java.lang.ClassLoader的loadClass()方法之中, 主要就是以下几个步骤 1、先检查类是否已经被加载过 2、若没有加载则调用父加载器的loadClass()方法进行加载 3、若父加载器为空则默认使用启动类加载器作为父加载器。 4、如果父类加载失败抛出ClassNotFoundException异常后再调用自己的findClass()方法进行加载。 如何主动破坏双亲委派机制 知道了双亲委派模型的实现那么想要破坏双亲委派机制就很简单了。 因为他的双亲委派过程都是在loadClass方法中实现的那么想要破坏这种机制那么就自定义一个类加载器重写其中的loadClass方法使其不进行双亲委派即可。 双亲委派被破坏的例子 双亲委派机制的破坏不是什么稀奇的事情很多框架、容器等都会破坏这种机制来实现某些功能。 第一种被破坏的情况是在双亲委派出现之前。 由于双亲委派模型是在JDK1.2之后才被引入的而在这之前已经有用户自定义类加载器在用了。所以这些是没有遵守双亲委派原则的。 第二种是JNDI、JDBC等需要加载SPI接口实现类的情况。 DriverManager是被根加载器加载的那么在加载时遇到以上代码会尝试加载所有Driver的实现类但是这些实现类基本都是第三方提供的根据双亲委派原则第三方的类不能被根加载器加载。 于是就在JDBC中通过引入ThreadContextClassLoader线程上下文加载器默认情况下是AppClassLoader的方式破坏了双亲委派原则。 第三种是为了实现热插拔热部署工具。为了让代码动态生效而无需重启实现方式时把模块连同类加载器一起换掉就实现了代码的热替换。 第四种时tomcat等web容器的出现。 Tomcat是web容器那么一个web容器可能需要部署多个应用程序。 不同的应用程序可能会依赖同一个第三方类库的不同版本但是不同版本的类库中某一个类的全路径名可能是一样的。 如多个应用都要依赖hollis.jar但是A应用需要依赖1.0.0版本但是B应用需要依赖1.0.1版本。这两个版本中都有一个类是com.hollis.Test.class。 如果采用默认的双亲委派类加载机制那么是无法加载多个相同的类。 所以Tomcat破坏双亲委派原则提供隔离的机制为每个web容器单独提供一个WebAppClassLoader加载器。 第五种时OSGI、Jigsaw等模块化技术的应用。 面试官java双亲委派机制及作用 概念当某个类加载器需要加载某个.class文件时它首先把这个任务委托给他的上级类加载器递归这个操作如果上级的类加载器没有加载自己才会去加载这个类。 例子当一个Hello.class这样的文件要被加载时。不考虑我们自定义类加载器首先会在AppClassLoader中检查是否加载过如果有那就无需再加载了。如果没有那么会拿到父加载器然后调用父加载器的loadClass方法。父类中同理也会先检查自己是否已经加载过如果没有再往上。注意这个类似递归的过程直到到达Bootstrap classLoader之前都是在检查是否加载过并不会选择自己去加载。直到BootstrapClassLoader已经没有父加载器了这时候开始考虑自己是否能加载了如果自己无法加载会下沉到子加载器去加载一直到最底层如果没有任何加载器能加载就会抛出ClassNotFoundException。 从上到下第一个ExtClassLoader 改为 BootstrapClassLoader 作用 防止重复加载同一个.class。通过委托去向上面问一问加载过了就不用再加载一遍。保证数据安全。保证核心.class不能被篡改。通过委托方式不会去篡改核心.class即使篡改也不会去加载即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个Class对象。这样保证了Class执行安全。 比如如果有人想替换系统级别的类String.java。篡改它的实现在这种机制下这些系统的类已经被Bootstrap classLoader加载过了为什么因为当一个类需要加载的时候最先去尝试加载的就是BootstrapClassLoader所以其他类加载器并没有机会再去加载从一定程度上防止了危险代码的植入。 5.沙箱安全机制 Java安全模型的核心就是Java沙箱(sandbox)什么是沙箱?沙箱是一个限制程序运行的环境。沙箱机制就是将Java代码限定在虚拟机(JVM)特定的运行范围中并且严格限制代码对本地系统资源访问通过这样的措施来保证对代码的有效隔离防止对本地系统造成破坏。沙箱主要限制系统资源访问那系统资源包括什么?CPU、内存、文件系统、网络。不同级别的沙箱对这些资源访问的限制也可以不一样。 ​ 所有的Java程序运行都可以指定沙箱可以定制安全策略。 ​ 在]ava中将执行程序分成本地代码和远程代码两种本地代码默认视为可信任的而远程代码则被看作是不受信的。对于授信的本地代码可以访问一切本地资源。而对于非授信的远程代码在早期的ava实现中安全依赖于沙箱(Sandbox)机制。如下图所示JDK1.0安全模型。 ​ 但如此严格的安全机制也给程序的功能扩展带来障碍比如当用户希望远程代码访问本地系统的文件时候就无法实现。因此在后续的Java1.1 版本中针对安全机制做了改进增加了安全策略允许用户指定代码对本地资源的访问权限。如下图所示JDK1.1安全模型。 ​ 在Java1.2版本中再次改进了安全机制增加了代码签名。不论本地代码或是远程代码都会按照用户的安全策略设定由类加载器加载到虚拟机中权限不同的运行空间来实现差异化的代码执行权限控制。如下图所示JDK1.2安全模型。 ​ 当前最新的安全机制实现则引入了域(Domain)的概念。虚拟机会把所有代码加载到不同的系统域和应用域系统域部分专门负责与关键资源进行交互而各个应用域部分则通过系统域的部分代理来对各种需要的资源进行访问。虚拟机中不同的受保护域(Protected Domain)对应不一样的权限(Permission)。存在于不同域中的类文件就具有了当前域的全部权限如下图所示最新的安全模型(jdk 1.6)。 组成沙箱的基本组件: 字节码校验器(bytecode verifier)︰确保Java类文件遵循lava语言规范。这样可以帮助java程序实现内存保护。但并不是所有的类文件都会经过字节码校验比如核心类。 类装载器(class loader) :其中类装载器在3个方面对Java沙箱起作用 。它防止恶意代码去干涉善意的代码; 。它守护了被信任的类库边界; 。它将代码归入保护域确定了代码可以进行哪些操作。 ​ 虚拟机为不同的类加载器载入的类提供不同的命名空间命名空间由一系列唯一的名称组成每一个被装载的类将有一个名字这个命名空间是由Java虚拟机为每一个类装载器维护的它们互相之间甚至不可见。 类装载器采用的机制是双亲委派模式。 1.从最内层VM自带类加载器开始加载外层恶意同名类得不到加载从而无法使用; 2.由于严格通过包来区分了访问域外层恶意的类通过内置代码也无法获得权限访问到内层类破坏代码就自然无法生效。 存取控制器(access controller)︰存取控制器可以控制核心API对操作系统的存取权限而这个控制的策略设定可以由用户指定。安全管理器(security manager)︰是核心API和操作系统之间的主要接口。实现权限控制比存取控制器优先级高。安全软件包(security package) : java.security下的类和扩展包下的类允许用户为自己的应用增加新的安全特性包括: 安全提供者消息摘要数字签名加密鉴别 6.Native关键字 编写一个多线程类启动。 public static void main(String[] args) { new Thread(()-{ },your thread name).start(); }点进去看start方法的源码 public synchronized void start() {if (threadStatus ! 0)throw new IllegalThreadStateException();group.add(this);boolean started false;try {start0(); // 调用了一个start0方法started true;} finally {try {if (!started) {group.threadStartFailed(this);}} catch (Throwable ignore) {}}}// 这个Thread是一个类这个方法定义在这里是不是很诡异看这个关键字nativeprivate native void start0();凡是带了native关键字的说明 java的作用范围达不到去调用底层C语言的库 JNIJava Native InterfaceJava本地方法接口 凡是带了native关键字的方法就会进入本地方法栈 Native Method Stack 本地方法栈 本地接口的作用是融合不同的编程语言为Java所用它的初衷是融合C/C程序Java在诞生的时候是C/C横行的时候想要立足必须有调用C、C的程序于是就在内存中专门开辟了一块区域处理标记为native的代码它的具体做法是 在 Native Method Stack 中登记native方法在 执行引擎( ExecutionEngine )执行的时候加载Native Libraies。 目前该方法使用的越来越少了除非是与硬件有关的应用比如通过Java程序驱动打印机或者Java系统管理生产设备在企业级应用中已经比较少见。因为现在的异构领域间通信很发达比如可以使用Socket通信也可以使用Web Service等等不多做介绍 7.程序计数器PC, Program Counter Register 程序计数器Program Counter Register 每个线程都有一个程序计数器是线程私有的就是一个指针指向方法区中的方法字节码(用来存储指向像一条指令的地址也即将要执行的指令代码)在执行引擎读取下一条指令是一个非常小的内存空间几乎可以忽略不计。 PC寄存器Program Counter Register是计算机体系结构中的一个重要概念特别是在处理器的执行过程中。它通常用于指示处理器当前正在执行的指令的位置或下一条要执行的指令的位置。 它是一块很小的内存空间,几乎可以忽略不计,也是运行速度最快的存储区域. 在 JVM 规范中,每个线程都有它自己的程序计数器,是线程私有的,生命周期与线程生命周期保持一致. 程序计数器会存储当前线程正在执行的 Java 方法的 JVM 指令地址. 它是程序控制流的指示器,分支,循环,跳转,异常处理,线程恢复等基础功能都需要依赖这个计数器来完成. 它是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域 8.栈 8.1 Java 虚拟机栈Java Virtual Machine Stacks 早期也叫 Java 栈描述的是 Java 方法执行的内存模型每个方法在执行的同时都会创建一个线帧Stack Frame用于存储局部变量表、操作数栈、动态链接、方法出口等信息每个方法从调用直至执行完成的过程都对应着一个线帧在虚拟机栈中入栈到出栈的过程。 栈的特点 栈是一种快速有效的分配存储方式,访问速度仅次于程序计数器. JVM 直接对 java 栈的操作只有两个:调用方法 入栈 .执行结束后 出栈 . 对于栈来说不存在垃圾回收问题. 栈中会出现异常,当线程请求的栈深度大于虚拟机所允许的深度时 , 会出现StackOverflowError. 栈的运行原理 JVM 对 Java 栈的操作主要是对栈帧的入栈和出栈遵循先进后出后进先出的原则。在一条活动的线程中只会有一个活动栈即当前执行的方法的栈帧栈顶是有效的这个栈帧称为当前栈Current Frame对应的方法称为当前方法Current Method定义该方法的类称为当前类Current Class。执行引擎运行的所有字节码指令只针对当前栈帧进行操作。如果在方法中调用了其他方法会创建新的栈帧放在栈的顶端成为新的当前栈帧。不同线程中的栈帧不允许相互引用即不可能在一个栈中引用另一个线程的栈帧。当前方法调用其他方法后在方法返回时当前栈帧会传回此方法的执行结果给前一个栈帧然后丢弃当前栈帧使前一个栈帧重新成为当前栈帧。Java 方法有两种返回方式正常的函数返回使用 return 指令抛出异常也会导致栈帧被弹出。 栈的内部结构 局部变量表Local Variables 用于存储方法参数和方法内部定义的局部变量。对于基本数据类型的变量直接存储其值对于引用类型的变量存储指向对象的引用。 操作数栈Operand Stack或表达式栈 用于表达式求值。在方法执行过程中实际上是不断执行语句并进行计算的过程这些计算过程都借助于操作数栈来完成。 动态链接Dynamic Linking或指向运行时常量池的方法引用 方法执行过程中可能需要使用类中的常量因此需要一个引用指向运行时常量池。 方法返回地址Return Address或方法正常/异常退出的定义 当方法执行完毕时需要返回到之前调用它的地方因此在栈帧中保存着方法返回地址。 其他辅助数据 如异常处理信息、synchronized块的锁信息等。 8.2 本地方法栈Native Method Stack 与虚拟机栈的作用是一样的只不过虚拟机栈是服务 Java 方法的而本地方法栈是为虚拟机调用 Native 方法服务的。 Java 虚拟机栈用于管理 Java 方法的调用而本地方法栈则用于管理本地方法的调用。本地方法栈也是线程私有的每个线程都有自己的本地方法栈。本地方法栈的内存大小可以被实现成固定的或者可动态扩展的与 Java 虚拟机栈类似。内存溢出的处理方式也相同。如果线程请求分配的栈容量超过本地方法栈允许的最大容量会抛出 StackOverflowError。本地方法是用 C 语言编写的因此需要一种机制将 Java 虚拟机中的 Java 方法与本地方法库中的本地方法进行连接。具体的做法是在 Native Method Stack 中登记 native 方法在 Execution Engine 执行时加载本地方法库从而实现本地方法的调用。 9.方法区Method Area 方法区基本理解 方法区概述 方法区是 Java 虚拟机内存的一部分它是被所有线程共享的内存区域。方法区主要用于存储加载的类字节码、类的元数据信息包括 class/method/field 等、static final 常量、static 变量、即时编译器编译后的代码等数据。方法区还包含一个重要的子区域即运行时常量池。 方法区与堆的关系 虽然方法区在逻辑上是堆的一部分但是在 HotSpot JVM 中方法区也被称为非堆目的是为了和堆区分开。在 HotSpot JVM 中方法区是一块独立于 Java 堆的内存空间。 方法区的作用 存储加载的类信息和元数据包括类的结构信息、方法、字段等。存储静态变量如 static 变量。存储常量包括 static final 常量。存储运行时常量池即类加载时将字面量和符号引用转换为直接引用的内存区域。 方法区的特点 线程共享方法区被所有线程共享因为它存储的是类的元数据信息而类是被所有线程共享的。非堆内存虽然在逻辑上是堆的一部分但是在 HotSpot JVM 中方法区被认为是非堆内存与 Java 堆区分开。 当 JVM 启动时方法区被创建。 它的物理内存空间可以与 Java 堆一样不连续。 方法区的大小可以选择固定或可扩展与堆空间类似。 方法区的大小决定了系统可以保存多少个类如果定义了太多类导致方法区溢出虚拟机会抛出内存溢出错误。 关闭 JVM 将释放方法区的内存。 方法区,栈,堆的交互关系 方法区用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。方法区是很重要的系统资源,是硬盘和 CPU 的中间桥梁,承载着操作系统和应用程序的实时运行. JVM 内存布局规定了 Java 在运行过程中内存申请,分配,管理的策略,保证了 JVM的高效稳定运行. 方法区是被所有线程共享所有字段和方法字节码以及一些特殊方法如构造函数接口代码也在此定义简单说所有定义的方法的信息都保存在该区域此区域属于共享区间; 静态变量、常量、类信息(构造方法、接口定义)、运行时的常量池存在方法区中但是实例变量存在堆内存中和方法区无关。 static final Class 常量池~ 存储类的结构信息、静态变量、常量、方法字节码等数据 方法区主要包含以下内容 类信息Class Information方法区存储了加载的类的元数据信息包括类的名称、父类的名称、类的修饰符、方法和字段的描述符等。这些信息对于JVM在运行时动态加载、链接和执行类非常重要。 静态变量Static Variables类的静态变量被存储在方法区中。这些变量在类加载时被分配内存并在整个程序执行期间保持不变。静态变量的生命周期与类的生命周期相同。 常量池Constant Pool方法区中包含了常量池用于存储类中的字面常量、符号引用等信息。常量池中的内容包括类和接口的全限定名、字段和方法的名称和描述符、字符串常量等。 方法字节码Method Bytecode类中的方法字节码被存储在方法区中。这些字节码被JVM解释执行或者编译成本地机器代码执行。 运行时常量池Runtime Constant Pool与常量池对应的是运行时常量池它是常量池的一部分用于存储编译时生成的字面常量和符号引用在类加载后被JVM转换为运行时数据结构。 过度使用静态变量、大量的类加载、频繁的字符串常量等操作都可能导致方法区内存溢出的问题。 10.三种JVM Sun公司HotSpot java Hotspot™64-Bit server vw (build 25.181-b13mixed mode)BEA JRockitIBM 39 VM我们学习都是Hotspot 11.堆Java Heap 概述 JVM 实例只有一个堆内存堆是 Java 内存管理的核心区域。Java 堆区在 JVM 启动时被创建其空间大小确定是 JVM 管理的最大内存块。堆内存大小可调节如 -Xms:10m堆起始大小和 -Xmx:30m堆最大内存大小。通常将起始值和最大值设置为一致以减少垃圾回收后重新分配堆内存大小的次数提高效率。根据《Java 虚拟机规范》堆可以处于物理上不连续的内存空间但在逻辑上应视为连续的。所有线程共享 Java 堆也可划分线程私有的缓冲区。《Java 虚拟机规范》指出所有对象实例都应在运行时分配在堆上。方法结束后堆中的对象不会立即移除只有在垃圾收集时才会被移除。堆是 GCGarbage Collection垃圾收集器执行垃圾回收的重点区域。 堆内存区域划分 Java8 及之后堆内存分为 :新生区(新生代)老年区(老年代) 新生区分为 Eden(伊甸园)区和 Survivor(幸存者)区 为什么分区(代) 将对象根据存活概率进行分类对存活时间长的对象放到固定区从而减少扫描垃圾时间及 GC 频率。 针对分类进行不同的垃圾回收算法对算法扬长避短。 对象创建内存分配过程 在 JVM 中为新对象分配内存是一个复杂而严谨的任务。设计者需要考虑内存分配的位置、算法以及与内存回收相关的问题包括内存碎片的产生。整个过程如下 新对象首先被分配到伊甸园区Eden该区域大小有限制。当伊甸园区Eden填满时如果程序需要创建更多对象JVM 的垃圾回收器将执行垃圾回收Minor GC销毁伊甸园区Eden中不再被引用的对象并将新对象放入伊甸园区Eden。剩余对象从伊甸园区Eden移动到幸存者 0 区to 区。如果再次触发垃圾回收上次存放在幸存者 0 区from 区的对象如果没有被回收将被移到幸存者 1 区to 区保证每次都有一个空的幸存者区to 区。如果再次经历垃圾回收存放在幸存者 0 区的对象会重新移到幸存者 0 区然后继续移到幸存者 1 区。当对象的 GC 年龄达到默认值通常为 15 次或者自定义的阈值时将会从新生代转移到老年代这个阈值可以通过参数 -XX:MaxTenuringThresholdN 设置。 在对象头中它是由 4 位数据来对 GC 年龄进行保存的所以最大值为 1111即为15。所以在对象的 GC 年龄达到 15 时就会从新生代转到老年代。 在老年区当内存不足时会触发 Major GC对老年区进行内存清理。如果在执行了 Major GC 后仍然无法保存对象就会导致 OutOfMemoryError 异常例如 Java.lang.OutOfMemoryError:Java heap space。 新生区与老年区配置比例 在 Java 虚拟机中可以通过调整堆结构的配置来优化内存使用其中包括新生代与老年代的占比以及伊甸园和幸存者空间的比例。 默认情况下新生代与老年代在堆结构中的占比由参数 -XX:NewRatio 控制默认值为 2。这表示新生代占 1老年代占 2新生代占整个堆的 1/3。如果需要调整可以设置为其他值例如 -XX:NewRatio4表示新生代占 1老年代占 4新生代占整个堆的 1/5。通过调整老年代的大小可以优化项目中生命周期较长对象的存储。 在 HotSpot 虚拟机中默认情况下伊甸园区Eden和两个幸存者空间Survivor的比例为 8:1:1。可以通过选项 -XX:SurvivorRatio 进行调整。例如设置 -XX:SurvivorRatio8表示调整幸存者空间的比例此时新生区的对象默认生命周期超过 15 次将会被转移到老年代进行养老。 分代收集思想 Minor GC、Major GC、Full GC 在 JVM 进行垃圾回收GC时并非每次都会同时回收新生代和老年代通常大部分情况下只会回收新生代。HotSpot VM 实现了两种主要类型的垃圾回收即部分收集和整堆收集 部分收集这种类型并非完整收集整个 Java 堆的垃圾而是针对特定区域的垃圾回收。部分收集又分为两种类型 新生代收集Minor GC / Yong GC仅回收新生代包括伊甸园区、幸存者区 S0 和 S1的垃圾。老年代收集Major GC / Old GC仅回收老年代的垃圾。 整堆收集Full GC这种类型涉及到整个 Java 堆包括新生代、老年代以及永久代或者元空间的垃圾回收。 整堆收集的出现情况包括 手动调用 System.gc(); 方法时。当老年代空间不足时。当方法区空间不足时。在开发期间应尽量避免整堆收集因为它会引起停顿时间较长的暂停影响程序的性能。 堆空间的参数设置 官网地址 | 参数 |描述 | | :–: | :–: | | -XX:PrintFlagsInitial | 查看所有参数的默认初始值 | | -XX:PrintFlagsFinal | 查看所有参数的最终值(修改后的值) | | -Xms | 初始堆空间内存(默认为物理内存的 1/64) | | -Xmx | 最大堆空间内存(默认为物理内存的 1/4) | | -Xmn | 设置新生代的大小(初始值及最大值) | | -XX:NewRatio | 配置新生代与老年代在堆结构的占比 | | -XX:SurvivorRatio | 设置新生代中 Eden 和 S0/S1 空间比例 | | -XX:MaxTenuringTreshold| 设置新生代垃圾的最大年龄 | | XX:PrintGCDetails | 输出详细的 GC 处理日志 | 字符串常量池为什么要调整位置JDK7: 永久代JDK7: 堆空间 提高回收效率避免永久代溢出 JDK7 及以后的版本中将字符串常量池放到了堆空间中。因为方法区的回收效率很低在 Full GC 的时候才会执行永久代的垃圾回收而 Full GC 是老年代的空间不足、方法区不足时才会触发。这就导致字符串常量池回收效率不高而我们开发中会有大量的字符串被创建回收效率低导致永久代内存不足。放到堆里能及时回收内存。 将字符串常量池移到堆空间中使得它可以享受到堆空间自动调整大小的优势避免了永久代内存溢出的问题。更好的 GC 控制 永久代的垃圾收集行为与新生代和老年代不同这增加了 GC 管理的复杂性。通过将字符串常量池移到堆空间中可以统一 GC 对整个堆空间的管理简化了 GC 管理的逻辑。 12.新生区、养老区 新生区是类诞生成长消亡的区域一个类在这里产生应用最后被垃圾回收器收集结束生命。 新生区又分为两部分伊甸区Eden Space和幸存者区Survivor Space所有的类都是在伊甸区被new出来的幸存区有两个0区 和 1区当伊甸园的空间用完时程序又需要创建对象JVM的垃圾回收器将对伊甸园区进行垃圾回收Minor GC。将伊甸园中的剩余对象移动到幸存0区若幸存0区也满了再对该区进行垃圾回收然后移动到1区那如果1区也满了呢这里幸存0区和1区是一个互相交替的过程再移动到养老区若养老区也满了那么这个时候将产生MajorGCFull GC进行养老区的内存清理若养老区执行了Full GC后发现依然无法进行对象的保存就会产生OOM异常 “OutOfMemoryError ”。如果出现 java.lang.OutOfMemoryErrorjava heap space异常说明Java虚拟机的堆内存不够原因如下 1、Java虚拟机的堆内存设置不够可以通过参数 -Xms初始值大小-Xmx最大大小来调整。 2、代码中创建了大量大对象并且长时间不能被垃圾收集器收集存在被引用或者死循环。 13.永久区Perm 永久存储区是一个常驻内存区域用于存放JDK自身所携带的ClassInterface的元数据也就是说它存储的是运行环境必须的类信息被装载进此区域的数据是不会被垃圾回收器回收掉的关闭JVM才会释放此区域所占用的内存。如果出现 java.lang.OutOfMemoryErrorPermGen space说明是 Java虚拟机对永久代Perm内存设置不够。一般出现这种情况都是程序启动需要加载大量的第三方jar包例如在一个Tomcat下部署了太多的应用。或者大量动态反射生成的类不断被加载最终导致Perm区被占满。 注意 常量池所在区域的变化为啥 \red{常量池所在区域的变化为啥} 常量池所在区域的变化为啥 − − 1 ) 提高回收效率避免永久代溢出 2 更好的 G C 控制 {--1)提高回收效率避免永久代溢出2更好的GC控制} −−1)提高回收效率避免永久代溢出2更好的GC控制 JDK1.6之前 有永久代常量池1.6在方法区 JDK1.7 有永久代但是已经逐步 “去永久代”常量池1.7在堆 JDK1.8及之后无永久代常量池1.8在元空间。 熟悉三区结构后方可学习JVM垃圾回收机制 实际而言方法区Method Area和堆一样是各个线程共享的内存区域它用于存储虚拟机加载的类信息普通常量静态常量编译器编译后的代码虽然JVM规范将方法区描述为堆的一个逻辑部分但它却还有一个别名叫做Non-Heap非堆目的就是要和堆分开。 对于HotSpot虚拟机很多开发者习惯将方法区称之为 “永久代Parmanent Gen”但严格本质上说两者不同或者说使用永久代实现方法区而已永久代是方法区相当于是一个接口interface的一个实现Jdk1.7的版本中已经将原本放在永久代的字符串常量池移走。 常量池Constant Pool是方法区的一部分Class文件除了有类的版本字段方法接口描述信息外还有一项信息就是常量池这部分内容将在类加载后进入方法区的运行时常量池中存放 14.堆内存调优 -Xms设置初始分配大小默认为物理内存的 “1/64”。-Xmx最大分配内存默认为物理内存的 “1/4”。-XX:PrintGCDetails输出详细的GC处理日志。 runtime.totalMemory() 这个方法返回Java虚拟机当前已经使用的内存量。它表示当前已经分配给Java虚拟机的内存量包括已使用的和尚未使用的部分。在Java程序运行期间随着内存的分配和释放这个值可能会不断变化。 runtime.maxMemory() 这个方法返回Java虚拟机试图使用的最大内存量。它表示Java虚拟机能够从操作系统获取的最大内存量。当Java程序运行时Java虚拟机会根据需要动态调整可用内存的大小但不会超过这个最大内存量。 runtime.freeMemory() 这个方法返回Java虚拟机当前空闲的内存量即还没有被分配给程序使用的内存。 它表示Java虚拟机中尚未被分配的、可用于新对象分配的内存量。 在Java程序运行期间这个值可能会随着程序的内存分配和释放而不断变化。 测试1 代码测试 默认情况下:分配的总内存是电脑内存的1/4,初始化的内存是电脑的1/64 public class Test {public static void main(String[] args) {Runtime runtime Runtime.getRuntime();System.out.println(runtime.availableProcessors(): runtime.availableProcessors());double maxMemory (double) runtime.maxMemory();double totalMemory (double) runtime.totalMemory();double freeMemory (double) runtime.freeMemory();double usedMemory totalMemory - freeMemory;System.out.println(runtime.maxMemory(): maxMemory bytes, (maxMemory / 1024) KB, (maxMemory / 1024 / 1024) MB, (maxMemory / 1024 / 1024 / 1024) GB);System.out.println(runtime.totalMemory(): totalMemory bytes, (totalMemory / 1024) KB, (totalMemory / 1024 / 1024) MB);System.out.println(runtime.freeMemory(): freeMemory bytes, (freeMemory / 1024) KB, (freeMemory / 1024 / 1024) MB);System.out.println(usedMemory: usedMemory bytes, (usedMemory / 1024) KB, (usedMemory / 1024 / 1024) MB);} }runtime.availableProcessors(): 16 runtime.maxMemory(): 3.754426368E9bytes, 3666432.0KB, 3580.5MB, 3.49658203125GB runtime.totalMemory(): 2.53231104E8bytes, 247296.0KB, 241.5MB runtime.freeMemory(): 2.47940152E8bytes, 242129.0546875KB, 236.45415496826172MB usedMemory: 5290952.0bytes, 5166.9453125KB, 5.045845031738281MBJVM参数缩写的含义–帮助记忆 JVM的参数缩写很容易混淆理解每个参数的具体含义可以帮助记忆。 VM选项有三种 : 标准VM选项VM规范的选项 -X: 非标准VM选项不保证所有VM支持 -XX: 高级选项高级特性但属于不稳定的选项 对于第二类参数其语义分别是 -Xms: 堆的初始化初始化大小助记memory startup -Xmx: 堆的最大内存数等同于-XX:MaxHeapSize助记memory maximum -Xmn: 堆中新生代初始及最大大小如果需要进一步细化初始化大小用-XX:NewSize最大大小用-XX:MaxNewSize助记memory nursery/new -Xss: 线程栈大小等同于-XX:ThreadStackSize助记stack size JVM参数缩写的含义–帮助记忆_jvm参数 x是什么意义-CSDN博客 IDEA中进行VM调优参数设置然后启动。 发现默认的情况下分配的内存是总内存的 1/4而初始化的内存为 1/64 VM options: -Xms1024m -Xmx1024m -XX:PrintGCDetailsVM参数调优把初始内存和总内存都调为 1024M运行查看结果 来大概计算分析一下 305664 K ( P S Y o u n g G e n ) 699392 K ( P a r O l d G e n ) 1005056 K B 981.5 M B 305664K(PSYoungGen) 699392K(ParOldGen) 1005056KB 981.5MB 305664K(PSYoungGen)699392K(ParOldGen)1005056KB981.5MB 再次证明元空间并不在虚拟机中而是使用本地内存。逻辑上存在物理上不存在 测试2 代码 package github.JVM.Demo02;import java.util.Random;/** * author subeiLY * create 2021-06-08 10:22 */ public class Demo02 {public static void main(String[] args) {String str suneiLY;while (true) {str str new Random().nextInt(88888888) new Random().nextInt(999999999);}} }vm参数 -Xms8m -Xmx8m -XX:PrintGCDetails测试查看结果 这是一个young 区域撑爆的JAVA 内存日志其中 PSYoungGen 表示 youngGen分区的变化, 1536k 表示 GC 之前的大小, 488k 表示GC 之后的大小。 整个Young区域的大小从 1536K 到 672K , young代的总大小为 7680K。 user – 总计本次 GC 总线程所占用的总 CPU 时间。 sys – OS 调用 or 等待系统时间。 real – 应用暂停时间。 如果GC 线程是 Serial Garbage Collector 串行搜集器的方式的话只有一条GC线程, real time 等于user 和 system 时间之和。 通过日志发现Young的区域到最后 GC 之前后都是0old 区域 无法释放最后报堆溢出错误。 其他文章链接 一文读懂 - 元空间和永久代Java方法区、永久代、元空间、常量池详解 15.GC 1.Dump内存快照 ​ 在运行java程序的时候有时候想测试运行时占用内存情况这时候就需要使用测试工具查看了。在eclipse里面有 Eclipse Memory Analyzer tool(MAT)插件可以测试而在idea中也有这么一个插件就是JProfiler一款性能瓶颈分析工具 作用 分析Dump文件快速定位内存泄漏 获得堆中对象的统计数据 获得对象相互引用的关系 采用树形展现对象间相互引用的情况 安装JProfiler IDEA插件安装 安装JProfiler监控软件 下载地址ej-technologies - Java APM, Java Profiler, Java Installer Builder 下载完双击运行选择自定义目录安装点击Next。 注意安装路径建议选择一个文件名中没有中文没有空格的路径 否则识别不了。然后一直点Next。 注册 // 注册码仅供大家参考 L-Larry_Lau163.com#23874-hrwpdp1sh1wrn#0620 L-Larry_Lau163.com#36573-fdkscp15axjj6#25257 L-Larry_Lau163.com#5481-ucjn4a16rvd98#6038 L-Larry_Lau163.com#99016-hli5ay1ylizjj#27215 L-Larry_Lau163.com#40775-3wle0g1uin5c1#0674配置IDEA运行环境 Settings–Tools–JProflier–JProflier executable选择JProfile安装可执行文件。如果系统只装了一个版本 启动IDEA时会默认选择保存。 代码测试 package github.JVM.Demo02;import java.util.ArrayList;/** * author subeiLY * create 2021-06-08 11:13 */ public class Demo03 {byte[] byteArray new byte[1*1024*1024]; // 1M 1024Kpublic static void main(String[] args) {ArrayListDemo03 list new ArrayList();int count 0;try {while (true) {list.add(new Demo03()); // 问题所在count count 1;}} catch (Error e) {System.out.println(count: count);e.printStackTrace();}} }vm参数 -Xms1m -Xmx8m -XX:HeapDumpOnOutOfMemoryError 寻找文件 使用 Jprofiler 工具分析查看 双击这个文件默认使用 Jprofiler 进行 Open大的对象 大对象 Thread Dump – 线程转储 从软件开发的角度上dump文件就是当程序产生异常时用来记录当时的程序状态信息例如堆栈的状态用于程序开发定位问题。 2.GC四大算法 0.引用计数法 每个对象有一个引用计数器当对象被引用一次则计数器加1当对象引用失效一次则计数器减1对于计数器为0的对象意味着是垃圾对象可以被GC回收。 目前虚拟机基本都是采用可达性算法从GC Roots 作为起点开始搜索那么整个连通图中的对象边都是活对象对于GC Roots 无法到达的对象变成了垃圾回收对象随时可被GC回收。 引用计数法Reference Counting是一种简单的垃圾回收算法它基于一个简单的概念每个对象都有一个与之相关的引用计数用于记录指向该对象的引用数量。当引用计数归零时说明没有任何指针指向该对象可以将其视为垃圾并进行回收。 这个算法的实现比较直观和简单它的主要优点包括 实时性 由于对象的回收与引用计数的增减直接相关一旦引用计数归零对象就会立即被回收不需要等待垃圾回收器的运行。 部分对象生命周期的确定性 对于一些循环引用的情况引用计数法可以及时地回收这些循环引用的对象而不需要等待整个循环成为不可达。 然而引用计数法也存在一些缺点 循环引用问题 如果两个或多个对象之间形成了循环引用即使它们已经不再被程序使用它们的引用计数也不会归零导致这些对象永远无法被回收从而造成内存泄漏。 性能开销 每次增减引用计数都需要额外的操作包括增减操作本身以及更新引用计数可能带来的并发控制开销。这会导致一定的性能开销并且可能影响程序的运行效率。 无法处理循环引用 由于无法处理循环引用问题引用计数法通常需要与其他垃圾回收算法如标记-清除、复制、标记-整理等结合使用以解决循环引用导致的内存泄漏问题。 1.复制算法(Copying) 年轻代中使用的是Minor GC采用的就是复制算法Copying。 什么是复制算法 算法的执行过程如下 对象分配阶段 初始时所有的新对象都会被分配到 Eden 区。 Minor GC 触发 当 Eden 区填满时会触发 Minor GC也称为新生代 GC。 存活对象复制 在 Minor GC 过程中GC 首先会检查所有存活的对象并将它们复制到 Survivor 区中的一个。如果一个对象经过一次 Minor GC 后仍然存活它就会被复制到另一个 Survivor 区而不是被清除。通常来说每次 Minor GC 后存活的对象都会被复制到一个新的 Survivor 区。 年龄增加 经过多次 Minor GC 后存活的对象会逐渐增加年龄。一般来说每经过一次 Minor GC对象的年龄就会增加一岁。当一个对象的年龄达到一定阈值通常是 15 岁它就会被晋升到老年代Old Generation。 Major GC 触发 在老年代空间不足或进行 Full GC全局垃圾回收时会触发 Major GC老年代 GC。这时会对整个堆内存进行清理和整理。 复制算法的优点包括 解决了内存碎片问题由于每次回收后存活对象都会被移动到一个新的区域因此不会产生内存碎片。简单高效实现简单不需要进行复杂的内存整理操作同时也可以高效地回收大量的短生命周期对象。 然而复制算法的缺点也是比较明显的 双倍内存消耗由于需要将存活对象复制到另一个区域因此需要额外的内存空间。无法处理长生命周期对象对于长时间存活的对象需要经过多次 Minor GC 后才会被晋升到老年代这可能会导致老年代的内存占用过高。 Minor GC 会把Eden中的所有活的对象都移到Survivor区域中如果Survivor区中放不下那么剩下的活的对象就被移动到Old generation中也就是说一旦收集后Eden就是变成空的了 当对象在Eden包括一个Survivor区域这里假设是From区域出生后在经过一次Minor GC后如果对象还存活并且能够被另外一块Survivor区域所容纳 上面已经假设为from区域这里应为to区域即to区域有足够的内存空间来存储Eden 和 From 区域中存活的对象则使用复制算法将这些仍然还活着的对象复制到另外一块Survivor区域即 to 区域中然后清理所使用过的Eden 以及Survivor 区域即form区域并且将这些对象的年龄设置为1以后对象在Survivor区每熬过一次MinorGC就将这个对象的年龄 1当这个对象的年龄达到某一个值的时候默认是15岁通过- XX:MaxTenuringThreshold 设定参数这些对象就会成为老年代。 -XX:MaxTenuringThreshold 任期门槛设置对象在新生代中存活的次数 面试题如何判断哪个是to区呢一句话谁空谁是to 原理解释 年轻代中的GC主要是复制算法Copying HotSpot JVM 把年轻代分为了三部分一个 Eden 区 和 2 个Survivor区from区 和 to区。默认比例为 8:1:1一般情况下新创建的对象都会被分配到Eden区一些大对象特殊处理这些对象经过第一次Minor GC后如果仍然存活将会被移到Survivor区对象在Survivor中每熬过一次Minor GC 年龄就会增加1岁当它的年龄增加到一定程度时就会被移动到年老代中因为年轻代中的对象基本上 都是朝生夕死所以在年轻代的垃圾回收算法使用的是复制算法复制算法的思想就是将内存分为两块每次只用其中一块当这一块内存用完就将还活着的对象复制到另外一块上面。复制算法不会产 生内存碎片 在GC开始的时候对象只会在Eden区和名为 “From” 的Survivor区Survivor区“TO” 是空的紧接着进行GCEden区中所有存活的对象都会被复制到 “To”而在 “From” 区中仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值的对象会被移动到老年代中没有达到阈值的对象会被复制到 “To 区域”经过这次GC后Eden区和From区已经被清空这个时候 “From” 和 “To” 会交换他们的角色 也就是新的 “To” 就是GC前的“From” 新的 “From” 就是上次GC前的 “To”。不管怎样都会保证名为To 的Survicor区域是空的。 Minor GC会一直重复这样的过程。直到 To 区 被填满 “To” 区被填满之后会将所有的对象移动到老年代中。 因为Eden区对象一般存活率较低一般的使用两块10%的内存作为空闲和活动区域而另外80%的内存则是用来给新建对象分配内存的。一旦发生GC将10%的from活动区间与另外80%中存活的Eden 对象转移到10%的to空闲区域接下来将之前的90%的内存全部释放以此类推 好处没有内存碎片坏处浪费内存空间。 劣势 复制算法它的缺点也是相当明显的。 1、他浪费了一半的内存这太要命了。2、如果对象的存活率很高我们可以极端一点假设是100%存活那么我们需要将所有对象都复制一遍并将所有引用地址重置一遍。复制这一工作所花费的时间在对象存活率达到一定程度时将会变的不可忽视所以从以上描述不难看出。复制算法要想使用最起码对象的存活率要非常低才行而且 最重要的是我们必须要克服50%的内存浪费。 2.标记清除(Mark-Sweep) 如何选择Eden区复制到Survivor区的对象---- 标记清除 标记存活对象 GC 会从根对象开始通过可达性分析Reachability Analysis标记所有在程序执行过程中仍然是活动的对象。这些根对象可能是线程栈中的引用、静态变量、本地变量表中的引用等。所有与这些根对象直接或间接相连的对象都会被标记为存活对象。 复制到 Survivor 区 在标记阶段结束后GC 会遍历 Eden 区中的所有对象将其中标记为存活的对象复制到 Survivor 区的其中一个。通常情况下复制后的对象是按照它们在 Eden 区中的顺序复制到 Survivor 区的但具体实现可能有所不同。 清空 Eden 区 复制完所有存活对象后Eden 区中所有未被复制的对象都会被认定为垃圾并且整个 Eden 区将被清空以便为后续的对象分配提供空间。 年龄增加 存活在 Survivor 区的对象的年龄会增加。每次经过一次 Minor GC存活对象会被复制到另一个 Survivor 区并且它们的年龄会增加。当达到一定的年龄阈值后对象会被晋升到老年代。 标记清除Mark-Sweep 回收时对需要存活的对象进行标记 回收不是绿色的对象。 当堆中的有效内存空间被耗尽的时候就会停止整个程序也被称为stop the world然后进行两项工作第一项则是标记第二项则是清除。 标记从引用根节点开始标记所有被引用的对象标记的过程其实就是遍历所有的GC Roots 然后将所有GC Roots 可达的对象标记为存活的对象。 清除 遍历整个堆把未标记的对象清除。 缺点这个算法需要暂停整个应用会产生内存碎片。两次扫描严重浪费时间。 用通俗的话解释一下 标记/清除算法就是当程序运行期间若可以使用的内存被耗尽的时候GC线程就会被触发并将程序暂停随后将依旧存活的对象标记一遍最终再将堆中所有没被标记的对象全部清 除掉接下来便让程序恢复运行。 劣势 首先、它的缺点就是效率比较低递归与全堆对象遍历而且在进行GC的时候需要停止应用 程序这会导致用户体验非常差劲 其次、主要的缺点则是这种方式清理出来的空闲内存是不连续的这点不难理解我们的死亡对象 都是随机的出现在内存的各个角落现在把他们清除之后内存的布局自然乱七八糟而为了应付 这一点JVM就不得不维持一个内存空间的空闲列表这又是一种开销。而且在分配数组对象的时 候寻找连续的内存空间会不太好找。 Q: 可达性分析Reachability Analysis具体过程 A: 可达性分析是垃圾回收算法中用于确定对象是否可访问或者说“可达”的一种技术。它的基本思想是从一组称为“根”的起始对象开始递归地查找所有通过引用链与根对象相连接的对象并将它们标记为可达的。而无法通过这些引用链到达的对象则被视为不可达的即可被回收的垃圾对象。 具体的可达性分析过程如下 标记根对象 可达性分析从一组称为根的对象开始。这些根对象通常包括 程序的活动线程的栈中引用的对象静态变量引用的对象特定于应用程序的任何其它对象被认为是程序的入口点。 递归遍历 从根对象开始递归地遍历所有与根对象直接或间接相关的对象。这一步骤通常使用深度优先搜索DFS或广度优先搜索BFS等算法来实现。 标记可达对象 在遍历的过程中将所有访问到的对象标记为可达。如果一个对象已经被标记为可达那么它的所有引用对象也会被继续遍历和标记。 清除不可达对象 一旦所有可达的对象都被标记完毕剩余的未被标记的对象即为不可达对象即垃圾对象。这些对象可以被安全地回收以释放其所占用的内存空间。 Q: 存活对象定义 A: 在可达性分析过程中存活对象指的是通过根对象可达的对象即那些可以被程序使用到的对象。垃圾对象则是不可达的对象即那些程序不再使用的对象可以被垃圾回收器回收的对象。 Q: 引用根节点是啥 A: 引用根节点是指可达性分析开始时所使用的起始对象集合。这些根节点通常包括程序中活动线程的栈帧中的引用、静态变量引用的对象、以及其他特定于应用程序的入口点对象。通过从这些根节点出发可达性分析可以遍历整个对象图并确定哪些对象是可达的哪些对象是不可达的。 3.标记压缩/标记整理Mark-Compact 标记整理说明老年代一般是由标记清除或者是标记清除与标记整理的混合实现。 什么是标记压缩 原理 在整理压缩阶段不再对标记的对象作回收而是通过所有存活对象都像一端移动然后直接清除边界以外的内存。可以看到标记的存活对象将会被整理按照内存地址依次排列而未被标记的内存会被 清理掉如此一来当我们需要给新对象分配内存时JVM只需要持有一个内存的起始地址即可这比维护一个空闲列表显然少了许多开销。 标记、整理算法 不仅可以弥补 标记、清除算法当中内存区域分散的缺点也消除了复制算法当中内存减半的高额代价 标记压缩是一种垃圾回收算法主要用于老年代Old Generation的内存区域旨在解决长生命周期对象的内存管理问题。与标记清除算法不同标记压缩算法在回收垃圾对象后会对存活对象进行整理以减少内存碎片化。 该算法一般包括以下步骤 标记存活对象 与标记清除算法相似标记压缩算法首先通过可达性分析标记所有存活的对象。 压缩存活对象 在标记阶段结束后存活对象通常会在堆内存中是不连续的这可能导致内存碎片的产生。为了解决这个问题标记压缩算法会将所有存活对象向堆内存的一端移动使它们成为连续的块。这个过程被称为压缩Compaction。 更新引用 在移动存活对象的过程中需要更新所有指向这些对象的引用以确保它们仍然指向正确的内存地址。 清除未标记的对象 在压缩存活对象后所有未被标记的对象被认为是垃圾并且可以被安全地回收。 标记压缩算法的优点包括 减少内存碎片化通过将存活对象整理成连续的块可以降低内存碎片的产生提高内存利用率。改善内存分配性能连续的内存块有利于快速、高效地分配内存空间。 然而标记压缩算法也有一些缺点 需要额外的移动操作将存活对象整理成连续的块需要额外的内存移动操作可能会增加垃圾回收的时间成本。不适用于所有场景对于内存分配频繁、存活对象较多的情况压缩操作可能会带来较大的性能开销。 尽管标记压缩算法在某些情况下效果显著但它通常用于老年代的垃圾回收而在新生代通常采用复制算法。 4.标记清除压缩(Mark-Sweep-Compact) 先标记清除几次再压缩。 3.总结 内存效率复制算法 标记清除算法 标记压缩算法 时间复杂度 内存整齐度复制算法 标记压缩算法 标记清除算法 内存利用率标记压缩算法 标记清除算法 复制算法 ​ 可以看出效率上来说复制算法是当之无愧的老大但是却浪费了太多内存而为了尽量兼顾上面所 提到的三个指标标记压缩算法相对来说更平滑一些 但是效率上依然不尽如人意它比复制算法多了一个标记的阶段又比标记清除多了一个整理内存的过程。 难道就没有一种最优算法吗 答案 无没有最好的算法只有最合适的算法 。 ----------- 分代收集算法 年轻代Young Gen 年轻代特点是区域相对老年代较小对象存活低。这种情况复制算法的回收整理速度是最快的。复制算法的效率只和当前存活对象大小有关因而很适 用于年轻代的回收。而复制算法内存利用率不高的问题通过hotspot中的两个survivor的设计得到缓解。 老年代Tenure Gen 老年代的特点是区域较大对象存活率高这种情况存在大量存活率高的对象复制算法明显变得不合适。一般是由标记清除或者是标记清除与标记整理的混合实现。Mark阶段的开销与存活对象的数量成正比这点来说对于老年代标记清除或 者标记整理有一些不符但可以通过多核多线程利用对并发并行的形式提标记效率。Sweep阶段的 开销与所管理里区域的大小相关但Sweep “就地处决” 的 特点回收的过程没有对象的移动。使其相对其他有对象移动步骤的回收算法仍然是是效率最好的但是需要解决内存碎片的问题。 16.JMM(Java Memory Model) 什么是JMM JMMJava Memory Model的缩写 他干嘛的官方其他人的博客对应的视频 作用缓存一致性协议用于定义数据读写的规则(遵守找到这个规则)。 JMM定义了线程工作内存和主内存之间的抽象关系∶线程之间的共享变量存储在主内存(Main Memory)中每个线程都有一个私有的本地内存Local Memory)。 解决共享对象可见性这个问题volilate 它该如何学习 JMM抽象的概念理论。 JMM对这八种指令的使用制定了如下规则java内存模型JMM理解整理 - 阿姆斯特朗回旋炮 - 博客园 不允许read和load、store和write操作之一单独出现。即使用了read必须load使用了store必须write。不允许线程丢弃他最近的assign操作即工作变量的数据改变了之后必须告知主存。不允许一个线程将没有assign的数据从工作内存同步回主内存。一个新的变量必须在主内存中诞生不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施use、store操作之前必须经过assign和load操作。一个变量同一时间只有一个线程能对其进行lock。多次lock后必须执行相同次数的unlock才能解锁。如果对一个变量进行lock操作会清空所有工作内存中此变量的值在执行引擎使用这个变量前必须重新load或assign操作初始化变量的值。如果一个变量没有被lock就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量。对一个变量进行unlock操作之前必须把此变量同步回主内存。 JMM对这八种操作规则和对volatile的一些特殊规则就能确定哪里操作是线程安全哪些操作是线程不安全的了。但是这些规则实在复杂很难在实践中直接分析。所以一般我们也不会通过上述规则进行分析。更多的时候使用java的happen-before规则来进行分析。 17. 执行引擎 Java 虚拟机JVM执行引擎是 Java 虚拟机的核心组件之一负责将 Java 字节码转换为机器码并执行程序。执行引擎通常包含解释器和即时编译器两种执行方式。 解释器Interpreter 解释器逐条解释执行 Java 字节码将字节码翻译成对应平台的机器指令然后由处理器执行。解释器的优点是简单、易于实现适用于快速启动和简单测试。然而解释执行通常速度较慢因为它需要动态地将每条字节码翻译为机器码。 即时编译器Just-In-Time CompilerJIT 即时编译器将字节码编译成本地机器码以提高执行速度。JIT 编译器可以选择性地将频繁执行的热点代码编译成机器码而不是每次都解释执行。这种方式可以显著提高程序的性能尤其是对于密集计算型和性能敏感的应用程序。 在实际中JVM 的执行引擎通常会结合解释器和即时编译器两种执行方式称为混合模式执行。在程序启动时解释器可以快速地启动并执行字节码同时即时编译器会监视程序的执行情况并且根据一定的触发条件对热点代码进行即时编译从而提高程序的整体性能。 另外现代的 JVM 还可以使用 Ahead-Of-TimeAOT编译器预先将整个应用程序编译成本地机器码以减少启动时间和减少运行时的开销。这种方式适用于一些对启动时间和运行时性能要求较高的场景例如移动设备、嵌入式系统等。 综上所述JVM 执行引擎通过解释器和即时编译器的组合以及可能的 AOT 编译器实现了 Java 程序的高效执行并且在性能和启动时间之间做出了权衡。 执行引擎除了包括解释器和即时编译器外还包括垃圾回收器Garbage CollectorGC它是 Java 虚拟机JVM中的另一个核心组件。 参考 Java方法区、永久代、元空间、常量池详解_java永久地址-CSDN博客 狂神说笔记——JVM入门07 - subeiLY - 博客园 java内存模型JMM理解整理 - 阿姆斯特朗回旋炮 - 博客园 JVMJava虚拟机-史上最全、最详细JVM笔记-CSDN博客 什么是Java内存模型 - 简书 深入理解JVM-内存模型jmm和GC - 简书
文章转载自:
http://www.morning.trsfm.cn.gov.cn.trsfm.cn
http://www.morning.rkxqh.cn.gov.cn.rkxqh.cn
http://www.morning.cxryx.cn.gov.cn.cxryx.cn
http://www.morning.hsksm.cn.gov.cn.hsksm.cn
http://www.morning.jqkrt.cn.gov.cn.jqkrt.cn
http://www.morning.trwkz.cn.gov.cn.trwkz.cn
http://www.morning.lgsfb.cn.gov.cn.lgsfb.cn
http://www.morning.mkczm.cn.gov.cn.mkczm.cn
http://www.morning.rkmsm.cn.gov.cn.rkmsm.cn
http://www.morning.pzbjy.cn.gov.cn.pzbjy.cn
http://www.morning.gwgjl.cn.gov.cn.gwgjl.cn
http://www.morning.cxtbh.cn.gov.cn.cxtbh.cn
http://www.morning.ntzfj.cn.gov.cn.ntzfj.cn
http://www.morning.ghfmd.cn.gov.cn.ghfmd.cn
http://www.morning.jhqcr.cn.gov.cn.jhqcr.cn
http://www.morning.madamli.com.gov.cn.madamli.com
http://www.morning.nhdw.cn.gov.cn.nhdw.cn
http://www.morning.brcdf.cn.gov.cn.brcdf.cn
http://www.morning.yrnrr.cn.gov.cn.yrnrr.cn
http://www.morning.lsnnc.cn.gov.cn.lsnnc.cn
http://www.morning.rqjxc.cn.gov.cn.rqjxc.cn
http://www.morning.c7624.cn.gov.cn.c7624.cn
http://www.morning.bynf.cn.gov.cn.bynf.cn
http://www.morning.rxydr.cn.gov.cn.rxydr.cn
http://www.morning.pfgln.cn.gov.cn.pfgln.cn
http://www.morning.wbdm.cn.gov.cn.wbdm.cn
http://www.morning.qdbcd.cn.gov.cn.qdbcd.cn
http://www.morning.clbzy.cn.gov.cn.clbzy.cn
http://www.morning.msgnx.cn.gov.cn.msgnx.cn
http://www.morning.lmrcq.cn.gov.cn.lmrcq.cn
http://www.morning.pffqh.cn.gov.cn.pffqh.cn
http://www.morning.ljdd.cn.gov.cn.ljdd.cn
http://www.morning.qlsyf.cn.gov.cn.qlsyf.cn
http://www.morning.pzbqm.cn.gov.cn.pzbqm.cn
http://www.morning.rkmhp.cn.gov.cn.rkmhp.cn
http://www.morning.dzfwb.cn.gov.cn.dzfwb.cn
http://www.morning.mkpqr.cn.gov.cn.mkpqr.cn
http://www.morning.rwjh.cn.gov.cn.rwjh.cn
http://www.morning.fbtgp.cn.gov.cn.fbtgp.cn
http://www.morning.glkhx.cn.gov.cn.glkhx.cn
http://www.morning.rnsjp.cn.gov.cn.rnsjp.cn
http://www.morning.smdiaosu.com.gov.cn.smdiaosu.com
http://www.morning.grxbw.cn.gov.cn.grxbw.cn
http://www.morning.sjwzl.cn.gov.cn.sjwzl.cn
http://www.morning.xrhst.cn.gov.cn.xrhst.cn
http://www.morning.krtky.cn.gov.cn.krtky.cn
http://www.morning.pyzt.cn.gov.cn.pyzt.cn
http://www.morning.ftmzy.cn.gov.cn.ftmzy.cn
http://www.morning.dqzcf.cn.gov.cn.dqzcf.cn
http://www.morning.gxtbn.cn.gov.cn.gxtbn.cn
http://www.morning.dzdtj.cn.gov.cn.dzdtj.cn
http://www.morning.qstkk.cn.gov.cn.qstkk.cn
http://www.morning.tftw.cn.gov.cn.tftw.cn
http://www.morning.tkryt.cn.gov.cn.tkryt.cn
http://www.morning.bfmq.cn.gov.cn.bfmq.cn
http://www.morning.qxwrd.cn.gov.cn.qxwrd.cn
http://www.morning.rkdhh.cn.gov.cn.rkdhh.cn
http://www.morning.lgpzq.cn.gov.cn.lgpzq.cn
http://www.morning.tkzqw.cn.gov.cn.tkzqw.cn
http://www.morning.txlxr.cn.gov.cn.txlxr.cn
http://www.morning.hphqy.cn.gov.cn.hphqy.cn
http://www.morning.wffxr.cn.gov.cn.wffxr.cn
http://www.morning.qzdxy.cn.gov.cn.qzdxy.cn
http://www.morning.hhskr.cn.gov.cn.hhskr.cn
http://www.morning.lbgsh.cn.gov.cn.lbgsh.cn
http://www.morning.qxmnf.cn.gov.cn.qxmnf.cn
http://www.morning.psdsk.cn.gov.cn.psdsk.cn
http://www.morning.btqqh.cn.gov.cn.btqqh.cn
http://www.morning.xrsqb.cn.gov.cn.xrsqb.cn
http://www.morning.gqbtw.cn.gov.cn.gqbtw.cn
http://www.morning.lpyjq.cn.gov.cn.lpyjq.cn
http://www.morning.flpjy.cn.gov.cn.flpjy.cn
http://www.morning.lcbt.cn.gov.cn.lcbt.cn
http://www.morning.pbygt.cn.gov.cn.pbygt.cn
http://www.morning.dmtbs.cn.gov.cn.dmtbs.cn
http://www.morning.zxhpx.cn.gov.cn.zxhpx.cn
http://www.morning.rcyrm.cn.gov.cn.rcyrm.cn
http://www.morning.tnjz.cn.gov.cn.tnjz.cn
http://www.morning.ngznq.cn.gov.cn.ngznq.cn
http://www.morning.klrpm.cn.gov.cn.klrpm.cn
http://www.tj-hxxt.cn/news/271610.html

相关文章:

  • 织梦修改网站后备份新出网页游戏
  • 平阳高端网站建设服务器怎么放网站吗
  • 网站开发按钮图片素材自己做的网站怎么爬数据库
  • 广西 南宁 微信微网站开发广西住房和城乡建设局官网
  • 用帝国cms做企业网站版权程序外包价格
  • 小松建设的官方网站企业管理咨询服务内容
  • 东莞网站的制作成立公司的好处
  • 网站开发技术文档包含台州网站建设费用
  • 云服务器发布网站自适应网站开发工具
  • php做网站参考文献通辽市工程建设网站
  • 方特网站是谁做的wordpress lovevideo
  • 为了做宣传网站而注册公司毕设做网站的系统概述怎么写
  • 娄底网站建设报价长沙做个网站多少钱
  • 使用cnnic证书的网站石家庄网站制作招聘
  • 网站做多少层级徐州建站程序
  • 如何做企业网站内容策划网站开发培训周末班
  • 西部数码虚拟主机怎么做网站推广策略及推广方式
  • html5网站源代码企业所得税优惠政策最新2022计算
  • 鹿邑建设局官方网站河北网站备案系统
  • 只做网站的人员工资惠州百度关键词优化
  • 做网站看好金石网络自助建站竹子
  • 做公司 网站百度爱采购怎么优化排名
  • 网站栅格布局广东网站建设制作价格低
  • wordpress建站吧建立电影网站教程
  • 网站服务器维护工具肥西县建设发展局网站
  • 上海做营销网站哪个公司好网页具有动画网站建设技术
  • 如何做二级域名子目录网站seo信息是什么
  • 网站做标准曲线网站建设免费的服务器
  • 服务器 空间 虚拟主机 网站需要重庆微信网站制作费用
  • 网站关键词一般设置几个争对银行排队做一网站