JVM
一、JVM内存机制
1. 类加载器子系统:将编号的.Class文件加载到JVM中 2. 运行时数据区:用于存储在JVM运行过程中产生的数据 3. 执行引擎:包括即时编辑器和垃圾回收器, (1)即时编辑器用于将java字节码编译成机器码; (2)垃圾回收器用于会搜运行贵哦城中不再使用的对象。 4. 本地接口库 5. 本地方法库
二、JVM多线程
- 虚拟机线程
- 周期性任务线程
- GC线程:垃圾回收
- 编译器线程:每种操作系统的解释器是不同的,但是基于解释器实现的虚拟机是相同的,所以Java能够跨平台
- 信号分发线程
三、JVM内存区域
1. 私有区域:程序计数器、虚拟机栈(每个线程都有自己的栈)、本地方法区 2. 共享区域:方法区(永久代)、堆(共享的、创建的对象和产生的数据都 被存储在堆中,是垃圾收集器进行垃圾回收的最主要的内存区域。从GC的角度分为:新生代、老年代、永久代) 3. 直接内存:虚拟机栈(私有的,可以理解为由栈针构成)
四、JVM类加载机制
(一)类加载过程
1.加载:将.class文件中的字节码文件通过类加载器加入内存 2.验证:验证字节流的文件格式,元数据,字节码,符号引用是否符合当前虚拟机要求 3.准备:为类变量(static修饰的字段变量)分配内存并且设置该类变量的初始值,这里不包含final修饰的static ,因为final在编译的时候就已经分配了。 4.解析:把常量池中的符号引用替换成直接引用。 5.初始化:如果该类具有父类就进行对父类进行初始化,执行其静态初始化器(静态代码块)和静态初始化成员变量。
(二)双亲委派机制
1.原理:如果一个类收到了类加载的请求,它并不会自己先去加载,而是把这个请求委托给父类加载器去执行,如果父类加载器还存在父类加载器,则进一步向上委托,依次递归,请求最后到达顶层的启动类加载器,如果弗雷能够完成类的加载任务,就会成功返回,倘若父类加载器无法完成任务,子类加载器才会尝试自己去加载,这就是双亲委派模式 2.优点:保障类的安全性和唯一性。
五、JVM垃圾回收
(一)JVM堆
1.新生代:占1/3,Eden区(8/10)、ServivorFrom区(1/10)、ServivorTo区(1/10) 2.老年代:占2/3 3.永久代:java8中被元数据取代
(二)GC过程
1.新生代:复制算法 java新创建的对象(如果属于大对象就直接分配到老年代)先保存在新生代的Eden区,当Eden区不够时,会存放在新生代的ServivorFrom区,如果ServivorFrom区内存也不够时就会出发MinorGC,将数据存入ServivorTo区,情况Eden区和ServivorFrom区中的对象,之后ServivorFrom和ServivorTo互换,原来的ServivorTo区变成下一次的GC时的ServivorFrom区 2.老年代:标记-清除算法 老年代的对象比较稳定,在进行MajorGC之前,会进行一次MinorGC,如果仍出现老年代空间不足的问题,则会触发MajorGC,在老年代没有内存空间分配时,会抛出OOM异常 3.永久代:java8中被元数据取代
(三)为什么要分Eden和Servivor区?
如果只有一个Eden区,那么每进行一次MinorGC就会有对象进入老年代,老年代的内存很快就会被填满,而进行MajorGC,老年代的内存远大于新生代内存,FullGC是对新生代和老年代进行内存清理,他的时间大约是MinorGC的十倍,所以要分为Eden和Servivor,从而减少送到老年代对象的数量,进而减少FullGC的发生Servivor的预筛选保证,只有经历16次MinorGC后依旧在新生代能够存活的对象才会被送往老年代
(四)为什么要设置两个Servivor区
解决碎片化问题,在Eden中的对象经历一次MinorGC后就会被存入ServivorFrom中,Eden中的对象就会被清除,下一次Eden存满时,Eden和Servivor中的对象将会存入ServivorTo中,然后清除Eden和ServivorFrom中的数据,再将ServivorFrom和ServivorTo进行互换(这个过程非常重要,因为这种复制算法保证了S1中来自S0和Eden两部分的存活对象占用连续的内存空间,避免了碎片化的发生)
(五)对象如何进入老年代
1.大对象直接进入老年代 2.如果对象在Eden中出生,如果经历了一次MinorGC后还存活,并且在Servivor中能够被容纳,则标记年龄为1,从此每经历过一次MinorGC年龄就会加1,如果复制次数(年龄达到15)就会被送入老年代
六、垃圾回收算法以及常用的垃圾回收器
(一)如何确定一个对象为垃圾
1.引用计数法:在对象添加一个引用的时候,引用计数+1,删除引用计数时-1,若一个对象没有被引用过,计数=0 缺点:循环问题。若两个对象相互引用,导致他们的引用一直存在,则不会被回收,所以在java中没有被采用,而被python采用 2.可达性分析 (1)首先定义一系列GCroots对象,然后以这些对象为起点,向下索引,若对象之间没有路径,则称该对象是不可达的,不可达对象至少要经历两次标记才会被判断是否被回收,若两次标记后仍不可达,则将会被回收 (2)GCroots的选择: 虚拟机栈中引用的对象 方法区中的类静态属性引用的对象 方法区中常量引用的对象 本地方法栈中Native方法引用的对象
(二)垃圾回收算法
1.标记-清除算法:最基础的算法,标记出所有需要被回收的对象,然后清除; 缺点:容易造成碎片化 2.复制算法:准备两块大小相等的空间,在第一片空间装满时,就将还活着的对象复制到第二片空间,再将已使用的空间一次性清除掉; 缺点:虽解决了碎片化的问题,但是他将内存分成两部分,降低了内存的的空间的使用,如果对象存活数量过多,复制算法的效率会大大降低 3.标记-整理算法:将存活下的对象标记后,移动到一侧,清除掉边缘之外的所有空间 4.分代收集算法:大部分javaGC采用的算法(复制算法+标记-整理算法)
(三)垃圾回收器
1.新生代收集器: (1)Serial收集器:Serial收集器是最基本的、发展历史最悠久的收集器。 特点:最基本的收集器,单线程,简单高效 (2)ParNew收集器:ParNew收集器其实就是Serial收集器的多线程版本 特点:Serial的多线程版本 (3)Parallel Scavenge 收集器:与吞吐量关系密切,故也称为吞吐量优先收集器。 特点:多线程,可以控制吞吐量,并具有GC自适应调节策略
GC自适应调节策略:Parallel Scavenge收集器可设置-XX:+UseAdptiveSizePolicy
参数。当开关打开时不需要手动指定新生代的大小(-Xmn)、Eden与Survivor区的
比例(-XX:SurvivorRation)、晋升老年代的对象年龄(-XX:PretenureSizeThreshold)
等,虚拟机会根据系统的运行状况收集性能监控信息,动态设置这些参数以提供最优的
停顿时间和最高的吞吐量,这种调节方式称为GC的自适应调节策略。
Parallel Scavenge收集器使用两个参数控制吞吐量:
XX:MaxGCPauseMillis 控制最大的垃圾收集停顿时间
XX:GCRatio 直接设置吞吐量的大小。
2.老年代收集器 (1)Serial Old:Serial Old是Serial收集器的老年代版本。 特点:单线程、标记整理 (2)Parallel Old:是Parallel Scavenge收集器的老年代版本。 特点:多线程、标记整理 (3)CMS:一种以获取最短回收停顿时间为目标的收集器。 特点:基于标记整理算法实现,并发收集、低停顿 回收步骤:CMS收集器的内存回收过程是与用户线程一起并发执行的。 初始标记:标记GC Roots能直接到的对象。速度很快但是仍存在Stop The World问题。 并发标记:进行GC Roots Tracing 的过程,找出存活对象且用户线程可并发执行。 重新标记:为了修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录。仍然存在Stop The World问题。 并发清除:对标记的对象进行清除回收。 CMS的缺点: 对CPU资源非常敏感。 无法处理浮动垃圾,可能出现Concurrent Model Failure失败而导致另一次Full GC的产生。 因为采用标记-清除算法所以会存在空间碎片的问题,导致大对象无法分配空间,不得不提前触发一次Full GC。 3.G1收集器 特点如下: 并行与并发:G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU来缩短Stop-The-World停顿时间。部分收集器原本需要停顿Java线程来执行GC动作,G1收集器仍然可以通过并发的方式让Java程序继续运行。 分代收集:G1能够独自管理整个Java堆,并且采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果。 空间整合:G1运作期间不会产生空间碎片,收集后能提供规整的可用内存。 可预测的停顿:G1除了追求低停顿外,还能建立可预测的停顿时间模型。能让使用者明确指定在一个长度为M毫秒的时间段内,消耗在垃圾收集上的时间不得超过N毫秒。
(1)G1为什么能建立可预测的停顿时间模型? 因为它有计划的避免在整个Java堆中进行全区域的垃圾收集。G1跟踪各个Region里面的垃圾堆积的大小,在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region。这样就保证了在有限的时间内可以获取尽可能高的收集效率。
(2)G1与其他收集器的区别: 其他收集器的工作范围是整个新生代或者老年代、G1收集器的工作范围是整个Java堆。在使用G1收集器时,它将整个Java堆划分为多个大小相等的独立区域(Region)。虽然也保留了新生代、老年代的概念,但新生代和老年代不再是相互隔离的,他们都是一部分Region(不需要连续)的集合。
(3)G1收集器存在的问题: Region不可能是孤立的,分配在Region中的对象可以与Java堆中的任意对象发生引用关系。在采用可达性分析算法来判断对象是否存活时,得扫描整个Java堆才能保证准确性。其他收集器也存在这种问题(G1更加突出而已)。会导致Minor GC效率下降。
(4)G1收集器是如何解决上述问题的? 采用Remembered Set来避免整堆扫描。G1中每个Region都有一个与之对应的Remembered Set,虚拟机发现程序在对Reference类型进行写操作时,会产生一个Write Barrier暂时中断写操作,检查Reference引用对象是否处于多个Region中(即检查老年代中是否引用了新生代中的对象),如果是,便通过CardTable把相关引用信息记录到被引用对象所属的Region的Remembered Set中。当进行内存回收时,在GC根节点的枚举范围中加入Remembered Set即可保证不对全堆进行扫描也不会有遗漏。
(5)如果不计算维护 Remembered Set 的操作,G1收集器大致可分为如下步骤: 初始标记:仅标记GC Roots能直接到的对象,并且修改TAMS(Next Top at Mark Start)的值,让下一阶段用户程序并发运行时,能在正确可用的Region中创建新对象。(需要线程停顿,但耗时很短。) 并发标记:从GC Roots开始对堆中对象进行可达性分析,找出存活对象。(耗时较长,但可与用户程序并发执行) 最终标记:为了修正在并发标记期间因用户程序执行而导致标记产生变化的那一部分标记记录。且对象的变化记录在线程Remembered Set Logs里面,把Remembered Set Logs里面的数据合并到Remembered Set中。(需要线程停顿,但可并行执行。) 筛选回收:对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划。(可并发执行)
七、引用类型
1.强引用:java默认new 对象则为强引用,如果一个对象通过一串强引用链接可到达,即使内存不足,也不会回收该对象。 2.软引用:用来描述一些非必须,但仍有用的对象。内存足够时,软引用对象不会被回收,只有在内存不足时,系统会回收软引用对象,通常用于实现缓存 3.弱引用:随时可能被垃圾回收器回收,无论内存是否足够,只要JVM开始进行垃圾回收,那些被弱引用关联的对象都会被回收。 4.虚引用:虚引用是所有引用类最脆弱的一个,如果一个对象持有虚引用,那么这个对象随时可能被回收,甚至不能通过get方法来获得其指向的对象。虚引用唯一的作用是,当其指向的对象被回收后,自己被加入到引用队列,用做记录该引用指向的对象已被销毁。
八、java8元数据取代永久代的优点
GC不会在程序运行期间对永久代的内存进行清理,所以永久代的内存会随着Class文件的增加而增加,文件过多时,会抛出OOM异常。被元数据取代后,元数据并不使用虚拟机的内存,而是直接使用操作系统的内存。因此,元空间的大小不再受JVM内存的限制,只和操作系统的内存有关。
九、堆和栈的区别
物理地址 堆的物理地址分配对对象是不连续的。因此性能慢些。栈使用的是数据结构中的栈,先进后出的原则,物理地址分配是连续的。所以性能快。 内存分别 堆因为是不连续的,所以分配的内存是在运行期确认的,因此大小不固定。一般堆大小远远大于栈。栈是连续的,所以分配的内存大小要在编译期就确认,大小是固定的。 存放的内容 堆存放的是对象的实例和数组。因此该区更关注的是数据的存储。栈存放:局部变量,操作数栈,返回结果。该区更关注的是程序方法的执行。 静态变量放在方法区静态的对象还是放在堆。 程序的可见度 堆对于整个应用程序都是共享、可见的。 栈只对于线程是可见的。所以也是线程私有。他的生命周期和线程相同。
十、什么是栈帧
栈帧就是用来记录方法的执行过程,方法的执行和返回对应栈帧在虚拟机栈中的入栈和出栈(每个运行中的线程当前只有一个栈帧处于活动状态)。
十一、java会出现内存泄漏嘛
内存泄漏是指不再被使用的对象或者变量一直被占据在内存中。理论上来说,Java是有GC垃圾回收机制的,也就是说,不再被使用的对象,会被GC自动回收掉,自动从内存中清除。 但是,即使这样,Java也还是存在着内存泄漏的情况,java导致内存泄露的原因很明确:长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是java中内存泄露的发生场景。 这东西,不全是我自己写的,不会不理解的是网上找到的,由于来自很多不同的博主,所以没法一一贴出,感谢!我只是想自己复习用,见到错误多多提醒,见到相同处还望海涵
|