Java 面试 ->jvm

  1. jvm内存区域:方法区,堆,程序计数器,虚拟机栈,本地方法栈。方法区,堆属于线程共享;程序计数器,虚拟机栈,本地方法栈属于线程私有的;

  2. 方法区:方法区属于线程共享的内存区域,主要存储已经被虚拟机加载的类信息,常量,静态变量,编译器编译后的代码;当方法区无法满足内存分配时,会抛出 OutOfMemoryError异常;运行时常量池就在方法区内,主要是 用于存放编译期生成的各种字面量和符号引用,这部分内容将类在加载后进入方法区的运行时常量池中存放。运行时常量池相对于Class文件常量池的另外一个重要特征就是具备动态性,Java语言并不要求常量一定只有在编译期才能产生,也就是并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用得比较多的便是String类的intern()方法。

  3. 堆:jvm堆属于线程共享的区域,在虚拟机启动时创建,是Java虚拟机管理内存中最大的一块,主要用于存放对象实例,堆是垃圾回收器管理的主要区域。如果堆无法给程序分配内存时会抛出OutOfMemoryError,异常。

  4. 程序计数器:程序计数器属于线程私有的数据区域,是一块小的内存空间,主要记录当前线程所执行的字节码行号指示器。字节码解释器工作时,通过改变这个计数器的值来选取下一条需要执行的字节码指令。分支,循环,跳转,一常处理,线程恢复等基础功能都需要依赖程序基数器来完成。

  5. 栈:虚拟机栈属于线程私有的数据区域,与线程同时创建,总数与线程关联,代表方法执行的内存模型。每个方法执行时都会创建一个栈帧来存储方法的变量表,操作数栈,动态链接方法,返回值,返回地址等信息。每个方法从调用到结束对应栈帧的入栈和出站操作。

  6. 本地方法栈:本地方法栈属于线程的私有数据区域,主要与虚拟机用到的native方法相关。

  7. Java的四种引用:强引用,软引用,弱引用,虚引用

    • 强引用:如果一个对象存在强引用那么改对象就永远不会被垃圾回收器回收,直至抛出内存溢出异常;Object obj = new Object();就是一个强引用。

    • 软引用:当内存足够时就不会回收改对象,如果内存不够时就会回收该对象。软引用通过 SoftReference类实现例如

                        Object obj = new Object();

                        SoftReference softObj = new SoftReference(obj);

    • 若引用:只要垃圾回收器在自己的内存空间中检测到了,就会直接被回收。通过 WeakReference实现。

    • 虚引用:如果一个对像只被虚引用,那么就会随时被jvm回收。 通过PhantomReference,无发通过虚引用访问该对象任何属性的函数。虚引用仅仅提供了一种确保对象被finalize以后做某些机制。虚引用必须配合 ReferenceQueue使用。

       8. 垃圾回收算法:

    • 标记-清除算法  :标记清除算法是最基础的垃圾手机算法。算法分为“标记” ,和“清除” 两个阶段

      • 标记:标记出所有回收对象,遍历堆中的对象标记

      • 清除:标记完成后统一回收所有被标记的的对象,遍历堆中的对象删除

      • 不足之处:效率问题:标记和清除都需要遍历堆中的对象效率不高。空间问题:标记清除后会产生大量的不连续的内存碎片,空间碎片太多导致后面给大对象分配内存时,连续空间不够导致下一次GC,如果几次后还是不够的话就会导致一直GC最终导致oom。

    • 复制算法(新生代算法):复制算法可以解决标记-清除算发的效率问题。原理是将内存按照容量划分未大小相等的两块,每次只使用其中的一块,当这块内存需要来及回收时,会将此区域还存活的对象复制到另一块内存上,然后在把使用过的内存区域一次清理掉。这样做的好处就是每次回收都是对上半区整个区域进行回收操作,内存分配时也不会考虑内存碎片等复杂情况,只需要按照顺序分配就好了。

      • 现代的虚拟机都不会直接将新生代的内存分为两块区域,因为新生代的对象往往时创建很快回收也很快,所以将新生代内存分为3块区域,以hotspot为例,将虚拟机内存分为Eden,Survivor1,Survivor2三块区域,初始比例为8:1:1。在进行垃圾回收时将Eden区域和s1区的对象直接复制到s2区,然后将s1区+Eden剩余的对象全部清除,如果s2区的的空间不够则需要老年代担保,直接进入老年代,如果s2区空间足够多那么这些对象在s1和s2区来回复制15次(hotspot默认, 可由参数MaxTenuringThreshol更改)后就会直接进入老年代。

      • 缺点:在对象存活率比较高时回进行多次复制导致效率降低,因此更使用与新生代垃圾回收算法

    • 标记-整理算法(标记-压缩算法)(老年代回收算法):

      • 复制算法在存活率比较高时会存在多次复制操作,效率会降低。因此老年代一般不适用 复制算法。针对老年代的特点,提出了一种标记-整理算法。标记整理算法跟标记清除算法的过程基本一样,只不过最后不是直接清除而是将存活的对像向一端移动然后直接清理掉另一端的对象。

      • 标记-整理和标记清除算法的最大区别就是,将存活的对象全部移动到内存的一端了,这样相比标记清除算法直接清除掉的思路来说不会产生内存碎片。

    • 分代收集算法:

      • 当前的虚拟机基本上都采用分代收集算法,这种算法时根据对象的存活周期不同将内存划分为多块,一般把jvm堆划分为老年代和新生代。老年代中对象存活率高,因此一般使用标记-清除或者标记-整理算法。而新生代中对象创建的块也死去块所以一般采用复制算法。

        9. 哪些内存需要收集:

    • 引用计数算法: 引用计数算法是垃圾收集器中的早期算法。在这种算法中,堆中的每个对象实例都有一个引用计数,当一个对象被创建时就将该对象实例分配给一个变量,将该变量计数设置为1。当任何其他变量被赋值为这个对象的引用时   计数加1,当一个对象实例的某一个引用超过了该对象的生命周期或者被设置为一个新值时,该对象的计数器减一。任何引用计数器为0的对象实例都可以被当作来及回收。当一个对象被垃圾收集器收集时,他引用的任何对象实例的引用计数器减1。

      • 优点:引用计数器收集器可以很快的执行,交织在程序运行中。在程序对短时间打断不敏感的实时运行环境中适合。

      • 缺点:无法检测出循环引用。例如a引用b,b又引用a,那么a和b的引用计数永远不会为0。

    • 可达性分析算法:可达性分析算法是从离散数学中的图论引入的,程序把所有的引用关系看作一张图,从-个节点GC ROOT开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕后,剩余的节点则被认为是没有被引用到的节点,即无用节点,这些无用节点则将被判断为可回收对象。对象被确定回收前会经过两次标记,第一次:即经过可达性分析后没有GC ROOT相连接的引用链,则被进行第一次标记。第二次:第一次标记后会进行一次筛选,筛选条件是此对象是否有必要执行finalize方法,在finalize方法中没有与引用链建立关联关系的,则被第二次标记,经过两次标记的对象将会被回收

      • 在Java中可作为GC ROOT的对象包括一下几种1.虚拟机栈中的引用对象(栈中的本地变量表)2.方法区中类静态属性引用的对象  3.方法区中常量引用的对象  4. 本地方法栈中引用的对象(native 方法的引用变量)

      • 优点:解决循环引用问题,由于GC ROOT在对象图外,定义的起点,不会被对象图内的对象引用

      • 方法区的垃圾回收:方法区回收的对象主要又废弃常量和无用类,对于废弃常量可以使用可达性分析收集,但是对于无用类需要满足三个条件才可以收集:1.该类的所有实例都被回收。2.加载该类的classloader已经被回收。3.该类对用的class没有在任何地方被引用,即无法通过反射访问该类。

        10. GC GC主要有Minor GC  和Full GC (Major GC)

    • Minor GC   新生代的垃圾回收就是Minor GC 当Eden空间不够时就会触发一次Minor GC  ,Minor GC 完成后还存活的对象那个会被放到survivor ,并且给对象年龄加1,当年龄到达一定程度时就会晋升到老年代。Minor GC 为解决老年代到新生代的引用使用了卡表技术

    • Full GC 当老年代空间不够时就会进行一次Full GC 即Major GC  新生代和老年代同时回收

        11.常见的垃圾收集器 

    • 新生代的垃圾收集器

      • Serial 单线程 采用标记-复制算法 

      • Parallel Scavenge 多线程 标记-复制算法 Parallel Scavenge 与Parallel New 类似更注重于吞吐量,不能与CMS一起使用

      • Parallel New 多线程 标记复制算法

    • 老年代垃圾收集器

      • Serial Old 标记整理算法 单线程

      • Parallel Old 标记整理算法 多线程 

      • CMS 标记清除算法 可以在程序运行过程中进行垃圾回收。在并行收集失败后会采用其他两种垃圾收集器收集一次。CMS的缺点 :1.由于并发进行,CMS在收集与应用线程会同时会增加对堆内存的占用,也就是说,CMS必须要在老年代堆内存用尽之前完成垃圾回收,否则CMS回收失败时,将触发担保机制,串行老年代收集器将会以STW的方式进行一次GC,从而造成较大停顿时间。 2.标记清除算法无法整理空间碎片,老年代空间会随着应用时长被逐步耗尽,最后将不得不通过担保机制对堆内存进行压缩。

      • cms收集器 初始标记(STW) 只标记GCrooot 能直接关联的对象 并发标记 重新标记(STW) 标记在并发过程中因为程序运作而发生变化的对象 并发清理

      • G1 初始标记 (STW)2.并发标记 3.最终标记(STW)4.筛选回收(STW)

        • 并行 理由cpu多核的优势,减少STW时间 2.分带收集 3.整体看是基于标记整理算法,局部看是标记复制算法 4.可预测的停顿

  • 类加载机制

    • 类加载步骤 

      • loading 载入将Java字节码转换为二进制流,加载到内存中,并且生成class对象。

      • 验证 只有符合jvm规范的class才能被正确执行

      • 准备 为静态变量赋值

      • 解析 将常量池中的符号引用转化为直接引用

      • 初始化 真正执行 Java 代码 初始化出发条件 new 对象 ,调用类的静态方法,反射,子类初始化会加载父类,启动类。

    • 类加载顺序 

      • 父类静态代码块>子类静态代码块>父类构造代码块>父类属性和普通代码块>父类构造器>子类构造代码块>子类属性和普通代码>子类构造器

      • 静态资源在类的初始化中只会执行一次

    • 类加载器

      • Bootstrap ClassLoader 最顶层的类加载器 加载核心类库

      • Extension ClassLoader 扩展类加载器 加载jre/lib/ext 下面的类

      • Application Class Loader 加载classpath下的类

    • 双亲委派模式

        • 当一个类加载器收到加载任务时,会先委托给父类加载器去完成,只有当父类加载器无法完成加载任务时,才会尝试执行加载任务

        • 优点 避免重复加载,父类已经加载了就不需要子类在加载了

        • 防止代码注入

    • 分布式事务

      • 2阶段提交  请求阶段 

      • TCC 补偿事务

      • MQ 事务消息

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值