网站空间商拿不回数据,seo关键词优化怎么做,网页设计代码下载,房地产新闻报道JVM 内存划分
JVM 内存划分为 四个区域#xff0c;分别为 程序计数器、元数据区、栈、堆
程序计数器是记录当前指令执行到哪个地址
元数据区存储存储的是当前类加载好的数据#xff0c;包括常量池和类对象的信息#xff0c;.java 编译之后产生 .class 文件#xff0c;运…JVM 内存划分
JVM 内存划分为 四个区域分别为 程序计数器、元数据区、栈、堆
程序计数器是记录当前指令执行到哪个地址
元数据区存储存储的是当前类加载好的数据包括常量池和类对象的信息.java 编译之后产生 .class 文件运行代码的时候会把了类对象的信息保存在内存里其中类元信息包括类名、类的父类实现哪些接口类的权限public/private… 方法元信息方法名参数类型返回值… 类元信息和方法元信息都是类对象里面包含的。
栈分为本地方法栈和虚拟机栈本质上都是存储方法的栈帧每次调用方法的时候会进行压栈方法调用完会执行出栈操作本地方法栈执行的是C方法虚拟机栈执行的是 Java方法。
堆存放 new 出来的对象的实例。 Test t new Test(); new Test() 这个对象的实例一定是存储在堆上的。
t 需要分类讨论如果 t 是一个局部变量t 就是在栈上的如果 t 是 成员变量t 就是在堆上的如果 t 是 静态成员变量t 就是在元数据区里的。 元数据区 和 堆整个Java进程共用一份 程序计数器和栈每个线程有一份 在面试时,如果遇到谈一下栈和堆的话, 要注意是数据结构里的栈和堆, 还是JVM的栈和堆 类加载
1加载根据类的全限定名包名类名找到对应的 .class 文件然后打开文件将文件的内容读取到内存中里。 2验证解析和校验 .class 文件的内容是否合法如果合法就把这里的内容转化成结构化的数据 3准备给类对象申请 全0 的内存空间 4解析针对字符串常量进行初始化将 .class 文件中的字符串常量放入元数据区的常量池中 5初始化将类对象进行最终的初始化包括对类对象各种属性的填充类中的静态成员父类如果父类没有被加载过会触发父类的类加载。 类加载触发的时机为懒加载当Java代码需要用到哪个类才会触发哪个类的加载 双亲委派模型
双亲委派模型的根据JVM实现类加载的源码提出的也就是说是先有JVM的类加载源码才有后面的双亲委派模型。
JVM 默认提供了三种类加载器分别为 BootstrapClassLoader、ExtensionClassLoader、ApplicationClassLoader
你可以认为这三个类加载器的关系如下 它们之间的联系你可以认为是这三个类加载器都有一个引用指向上一个类加载器ApplationClassLoader 的父亲的 ExtensionClassLoaderExtensionClassLoader 的父亲是 BootstrapClassLoaderBootstrapClassLoader 的父亲为 null。
BootstrapClassLoader 负责加载Java 标准库的 .class 文件 ExtensionClassLoader 负责加载 Java 扩展库的 .class 文件 ApplicationClassLoader 负责加载 Java第三方库的.class 文件
随着时代的发展Java的扩展库我们很少使用取而代之的是Java第三方库例如使用maven 加载的库就是第三方库
双亲委派模型的执行流程 进行类加载通过全限定类名寻找 .class 文件先从 ApplicationClassLoader 最为入口开始然后将类加载任务委托上一级 ExtensionClassLoader 再将类加载任务委托给上一级 BootstrapClassLoader由于 BootstrapClsssLoader 没有上一级开始执行类加载任务将需要的Java标准库里的 .class 文件加载到内存里然后将任务下放给下一级 ExtensionClassLoader ExtensionClassLoader 将所需要的Java扩展库的 .class 文件加载到内存里最后将任务交给下一级 ApplicationClassLoaderApplicationClassLoader 负责加载Java第三方库的 .class 文件。
如果没有找到对应的 .class 文件就会抛出异常 ClassNotFoundException 垃圾回收 GC
在Java中你不需要手动释放内存这是因为JVM 内部实现了 GC也就是垃圾回收机制这个机制可以自动回收内存空间这使得Java程序员不需要考虑内存泄漏的问题。
垃圾回收有两个步骤首先就是找到垃圾然后再回收垃圾
如何找到垃圾
找到垃圾这里介绍两个算法一个是引用计数另一个是可达性分析
引用计数
在创建的对象旁边开辟一个空间用来存放该对象被多少个引用引用着当被引用的数量为 0 的时候就会进行垃圾回收 但这也有缺点 1内存消耗多尤其是对象本身比较小的时候引用计数占据的空间的比例就会很大例如假设对象自身占8个字节引用计数可能占 4 个字节
2可能出现 “ 循环引用” 的问题 假设一个类里面还定义了一个引用用这个类创建的两个对象的引用相互指向对方那这两个对象是不可能被垃圾回收掉的 当我们将 a 和 b 两个引用置为 null 的时候我们创建的两个对象的实例是不会被回收掉的因为此时两个对象都被对方引用着双方的引用计数均为 1这就是 “循环引用”
可达性分析JVM 使用
为了解决上述引用计数产生的两个弊端空间浪费和循环引用这里提出了可达性分析算法。
可达性分析算法采用时间换空间的策略这也是 JVM 寻找垃圾使用的算法
首先对代码的一些特定对象作为遍历的 “起点”这些特定的对象包括 栈上的局部变量引用变量、常量池引用指向的对象、静态成员引用类型的【这些对象在程序运行到任何时刻都容易被捕获到的】 【因为不会扫描堆上的对象所以不会存在因为 “循环引用” 而影响垃圾判断的现象】
然后进行遍历判断某个对象是否能被访问到在能访问到的对象标记为 “可达”。
当完成遍历后把未标记为 “可达” 的对象标记为 “不可达”
JVM 通过上述操作就知道哪些对象是 可达的哪些是 不可达的接下来就是回收垃圾的操作了。
举个例子
class Node {Node left;Node right;
}public class Test2 {public Node build() {Node a new Node();Node b new Node();Node c new Node();Node d new Node();Node e new Node();Node f new Node();Node g new Node();a.left b;a.right c;b.left d;b.right e;f.right g;return a;}
}由于方法最后只返回 a 这个节点这时候 f 和 g 节点就会被回收掉。
如何处理找到的垃圾
这里介绍四种处理方式标记–清除 算法复制算法标记整理 算法分代算法,其中最后一个算法 分代算法是 JVM 真正使用的。
标记–清除 算法
在需要回收的内存上做上标记然后进行回收清除操作这是最直接的算法但是存在一个缺点就是会产生内存碎片。
举个例子假设一共有 7 块内存块经过可行性分析算法得出 2、4、6 这三块内存为需要回收的内存先做上标记然后进行回收释放但是空出来的内存碎片可能难以得到利用。
标记整理算法
由于标记清除算法会产生大量的内存碎片标记整理算法直接将垃圾回收设计为类似顺序表删除的形式, 将非垃圾的对象排到一起, 这样就可以空出大块的空闲内存块了.
但是缺点也是显而易见的, 就是系统开销比较大, 尤其是移动较大的对象的时候.
复制算法
将内存空间一分为二当需要进行垃圾回收的时候将非垃圾的内存块依次紧凑地复制到另一半内存中。
但是也存在两个缺点 1内存消耗大内存的空间利用率低 2一旦非垃圾的内存块较多的时候复制的成本就会较高尤其是复制包含较大的对象的时候。
举个例子要回收 1 和 4 号内存块。
分代算法 (JVM 使用)
分代算法是结合了上面两种算法的优点取长补短的综合算法
JVM 将对象划分为 新生代和老年代新生代又划分了三个区域分别是伊甸区、两个幸存区【空间占比为8:1:1】 分代算法的代指的是对象的年龄大小, 年龄的大小和 GC 轮次相关 命名的来源于圣经人类发源地伊甸园在这里指新生成的对象的存放位置由于灭世洪水的降临人类打造了诺亚方舟成功躲避天灾的人门就是幸存者这里的幸存区是指对象经过GC之后还能存留下来的存储位置 我们认为对象一开始生成的时候是最容易被 GC 掉的所以新生成的对象保存在 伊甸区里经过 一轮GC 之后剩下的对象不会很多将这些幸存的对象放到幸存区里两个幸存区就是为了执行复制算法经过多轮 GC 之后还能在幸存区里存活的对象将放入到老年代。 一般来说,如果内存占用空间比较大的对象我们一般是直接放入老年代, 减少系统开销 新生代的 GC 频次较高 老年代的 GC 频次较低
因为我们认为要 GC 掉的早就 GC 掉了之所以能存活在老年代说明这个对象经常被使用也就是说老年代的对象不需要高频次的 GC 次数GC 的周期可以长一点减少系统开销。