一、内存模型以及分区,需要详细到每个区放什么,堆里面的分区以及各自的特点。 1 程序计数器:线程私有,记录当前线程执行字节码的行号指示器。 2 虚拟机栈:线程私有。虚拟机执行java方法时的内存模型,会创建一个“栈桢"。 存放操作数栈,局部变量表,方法出口,动态链接等信息。 3 本地方法栈:主要用于处理本地方法,会有native关键字标明,由c/c++编写。 里面的数据结构和虚拟机栈类似。 4 堆内存:线程共享。存储new出来的对象、全局变量等数据,是JVM管理的最大一块内存区域。与堆相关的一个重要概念就是垃圾收集器,现代几乎所有的垃圾收集器都采取分代收集算法。堆空间也对此进行了相应的划分:新生代与老年代。Eden空间、From Survivor空间和To Survivor空间。 5 方法区(Method Area):存储元信息(类的方法代码,变量名,方法名,访问权限,返回值)等。 JDK1.8之前使用永久代来实现方法区,jdk1.8开始后,废弃永久代,用元空间代替。运行时常量池是方法区的一部分内容。 6 直接/堆外内存:JVM通过堆上的DirectByteBuffer来操作直接内存。直接内存的大小并不受到java堆大小的限制,甚至不受到JVM进程内存大小的限制。它只受限于本机总内存(RAM及SWAP区或者分页文件)大小以及处理器寻址空间的限制。
二、对象创建方法,对象的内存分配,对象的访问定位。 对象创建方法步骤 1在堆内存中创建出对象的实例 当我们用new关键字来创建对象的实例时,JVM首先会检查new这个指令的参数是不是能到常量池中定位成一个类的符号引用,然后再检查该符号引用所对应的类是不是被正常的加载、连接、初始化了,如果没有则必须要完成类的加载过程。 当事先的准备阶段都结束之后,接着JVM则为该对象分配内存(对象加载完内存已经确定)。 而在Java堆中内存整体来说是分成2部分的,第一部分内存是已经被使用或者说已经被占用的,而第二部分内存则是空闲的可以被使用的,而已经被占用的空间和未被使用的空间又分为两种情况: 第一种:指针碰撞 在堆内存中已经截然有序的将已使用和未使用的内存空间给分离开了。堆中的空间通过一个指针进行分割,一侧是已经被占用的空间,另一侧是未被占用的空间。新创建的对象则会存在于未被占用的空间中,然后指针发生移动指向了下一个可以被使用的内存空间。 第二种:空闲列表 堆内存空间中已被使用与未被使用的空间是交织在一起的。这时,虚拟机就需要通过一个列表来记录哪些空间是可以使用的,哪些空间是已被使用的,接下来找出可以容纳下新创建对象的且未被使用的空间,在此空间存放该对象,同时还要修改列表上的记录。 出现两种情况的原因: 有一些垃圾收集器是带压缩过程,所谓带压缩过程是指垃圾收集器在执行一次垃圾回收的时候,除了把真正垃圾的对象给清除掉之外,此时已使用和未使用的内存一定是不连续的,那么它们在做完清除工作之后还要做一次对象的移动操作,也就是将已被使用的和未被使用的分文别类的给排开,此时就可以用指针碰撞的方式来解决对象存放的问题;而有些垃圾收集器在垃圾回收之后就立马结束了,不会对对象进行一个移动操作,从而导致已使用和未被使用的内存交织在一起的,此时就只能用空闲列表的方式来解决对象存放的问题啦。 2为对象的实例成员变量赋初值 3将对象的引用返回 对象在内存中的布局 1 对象头:存放对象自身的一些运行时的数据信息,比如说一个对象有一个hash码、还有分代的一个信息等 2 实例数据 3 对齐补充:起到占位符的作用 对象的访问定位 1使用句柄的方式 对象的位置如果移动,句柄中的对象指针也会发生改变,但是引用指向句柄不会改变。 2使用直接指针的方式 对象的位置发生改变,引用的值就会发生改变,但是还是可以通过这个引用来调用该实例对象。GC带压缩情况(指针碰撞情况,GC压缩未使用内存空间)下,这一种的效率更佳! 三、GC 的两种判定方法(如何判断一个对象是否存活?) 引用计数算法:给对象添加一个引用计数器,当有一个地方引用它,计数器加1,引用失效,计数器减1,计数器为0的对象就是不可能再被使用的对象,但是引用计数算法不能解决对象循环引用的问题。 跟搜索算法(可达性分析):在实际的生产语言当中都是使用跟搜索算法判断一个对象是否存活。基本思路是通过一系列称之为GcRoots的点作为起始向下搜索。当一个对象到GcRoots没有任何引用链相连则称该对象不再被使用。在Java语言中,GcRoots包括VM栈中的引用、方法区的静态引用、JNI(本地方法)的引用。
四、GC 的三种收集方法:标记清除、标记整理、复制算法的原理与特点,分别用在什么地方,如果让你优化收集方法,有什么思路? 标记清除算法:分为标记与清除两个阶段,首先标记出需要清除的对象,然后回收所有需要收集的对象。 特点 效率问题:标记和清除的两个过程效率都不高 空间问题:标记清理之后会产生大量不连续的内存碎片,空间碎片太多会导致后续使用中无法找到足够的连续内存而提前触发一次垃圾收集操作。 标记整理(压缩)算法:分为标记与清除两个阶段。标记过程一样,但是后续不是直接清理而是将存活的对象向一端移动,然后直接清理掉这段边界以外的内存。 特点 没有内存问题,但是比标记清楚算法耗费更多的时间来进行压缩 复制搜集算法:将内存区分为两半,每次只使用其中的一半。一半的内存用完了之后,仅将存活的对象复制到另一半的内存中,然后一次性清理原来的半区内存。 特点:将内存缩小为原来的一半 现代商业虚拟机都是用这一算法收集新生代,并将内存分为三个区 Eden:From Survivor:ToSurvivor=8:1:1
五、GC 收集器有哪些?CMS 收集器与 G1 收集器的特点 1.serial收集器 单线程,工作时必须暂停其他工作线程。多用于client机器上,使用复制算法 2.ParNew收集器 serial收集器的多线程版本,server模式下虚拟机首选的新生代收集器。复制算法 3.Parallel Scavenge收集器 复制算法,可控制吞吐量的收集器。吞吐量即有效运行时间。 4.Serial Old收集器 serial的老年代版本,使用整理算法。 5.Parallel Old收集器 第三种收集器的老年代版本,多线程,标记整理 6.CMS收集器 目标是最短回收停顿时间。标记清除算法实现,分四个阶段: 初始标记:GC Roots直连的对象做标记 并发标记:多线程方式GC Roots Tracing 重新标记:修正第二阶段标记的记录 并发清除。 7.G1收集器 “标记-整理”算法,可以非常精确的控制停顿,实现在基本不牺牲吞吐量的前提下完成低停顿的内存回收。G1将整个java堆(包括新生代、老年代)划分为多个固定大小的独立区域(Region),并跟踪这些区域的垃圾,维护一个优先列表,优先回收垃圾最多的区域。
六、类加载器双亲委派模型机制? 三个默认的ClassLoader: Bootstrap loader、ExtClassLoader、AppClassLoader 双亲委派机制的工作流程:
当前ClassLoader首先从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。每个类加载器都有自己的加载缓存,当一个类被加载了以后就会放入缓存,等下次加载的时候就可以直接返回了。当前ClassLoader的缓存中没有找到被加载的类的时候,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父类的父类去加载,一直到Bootstrap ClassLoader.当所有的父类加载器都没有加载的时候,再由当前的类加载器加载,并将其放入它自己的缓存中,以便下次有加载请求的时候直接返回。 “双亲委派”机制只是Java推荐的机制,并不是强制的机制。 我们可以继承java.lang.ClassLoader类,实现自己的类加载器。如果想保持双亲委派模型,就应该重写findClass(name)方法;如果想破坏双亲委派模型,可以重写loadClass(name)方法。七、Minor GC、Full GC 、Major GC? Minor GC:清理新生代;Major GC是清理老年代;Full GC 是清理整个堆,包括新生代和老年代 触发条件: Minor GC触发条件:当新生代无法为新生对象分配内存空间的时候,会触发Minor GC,比如Eden区满了会触发一次 Major GC触发条件:回收老年代,通常至少经历过一次Minor GC Full GC触发条件: (1)调用System.gc时,系统建议执行Full GC,但是不必然执行 (2)老年代空间不足 (3)方法区空间不足 (4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存 (5)由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小
八、什么是类加载器,类加载器有哪些? Java类加载器是Java运行时环境的一部分,负责动态加载类到Java虚拟机的内存空间中,类通常是按需加载。类加载器负责读取 编译过后的Java 字节代码,并转换成 java.lang.Class类的一个实例。三个默认的ClassLoader: Bootstrap loader、ExtClassLoader、AppClassLoader。 我们可以继承java.lang.ClassLoader类,实现自己的类加载器。如果想保持双亲委派模型,就应该重写findClass(name)方法;如果想破坏双亲委派模型,可以重写loadClass(name)方法。
九、简述 java 内存分配与回收策率 1.对象优先在Eden分配 2.大对象直接进入老年代 3.长期存活的对象将进入老年代 4.动态对象年龄判断 5.空间分配担保