吊打面试官----精华汇总之JVM

1.   描述一下 JVM 加载 Class 文件的原理机制?
2.   什么是类加载器?
3.   类加载器有哪些?
4.   什么是tomcat类加载机制?

前面3个类加载和默认的一致,CommonClassLoader、CatalinaClassLoader、SharedClassLoader和WebappClassLoader则是Tomcat自己定义的类加载器,它们分别加载 /common/*、/server/*、/shared/*(在tomcat 6之后已经合并到根目录下的lib目录下)和 /WebApp/WEB-INF/* 中的Java类库。其中WebApp类加载器和Jsp类加载器通常会存在多个实例,每一个Web应用程序对应一个WebApp类加载器,每一个JSP文件对应一个Jsp类加载器。

tomcat 违背了java 推荐的双亲委派模型了吗?违背了。
双亲委派模型要求除了顶层的启动类加载器之外,其余的类加载器都应当由自己的父类加载器加载。
显然,tomcat 不是这样实现,tomcat 为了实现隔离性,没有遵守这个约定,每个webappClassLoader加载自己的目录下的class文件,不会传递给父类加载器。
如果tomcat 的 Common ClassLoader 想加载 WebApp ClassLoader 中的类,该怎么办?可以使用线程上下文类加载器实现,使用线程上下文加载器,可以让父类加载器请求子类加载器去完成类加载的动作。

 

5.   类加载器双亲委派模型机制

一个 java 类的完整的生命周期会经历加载、连接、初始化、使用、和卸载五大阶段
加载:将.class字节码加载到jvm中,并生成一个class对象。
连接:进行验证(是否能被jvm执行,主要包括文件格式验证、元数据验证、字节码验证和符号引用验证)、
准备:为静态变量分配内存;
解析:把类中的符号引用转换为直接引用;
初始化:为类的静态变量赋予用户定义的默认值和执行静态代码块;
在例子中,在加载阶段将ClassLoaderTest加载到jvm中;在连接阶段对静态变量分配内存空间并初始化默认值,此时classLoaderTest = null;a =0;b=0;
main方法中通过
        ClassLoaderTest instance = getInstance();


待完善;

 

6.   Java 内存分配?

 

首先要说一下JVM内存空间分为五部分,分别是:方法区、堆、Java虚拟机栈、本地方法栈、程序计数器

方法区主要用来存放类信息、类的静态变量、常量、运行时常量池等,方法区的大小是可以动态扩展的,

主要存放的是数组、类的实例对象、字符串常量池等。

Java虚拟机栈是描述JAVA方法运行过程的内存模型,Java虚拟机栈会为每一个即将执行的方法创建一个叫做“栈帧”的区域,该区域用来存储该方法运行时需要的一些信息,包括:局部变量表、操作数栈、动态链接、方法返回地址等。比如我们方法执行过程中需要创建变量时,就会将局部变量插入到局部变量表中,局部变量的运算、传递等在操作数栈中进行,当方法执行结束后,这个方法对应的栈帧将出栈,并释放内存空间。栈中会发生的两种异常,StackOverFlowError和OutOfMemoryError,StackOverFlowError表示当前线程申请的栈超过了事先定好的栈的最大深度,但内存空间可能还有很多。 而OutOfMemoryError是指当线程申请栈时发现栈已经满了,而且内存也全都用光了。

本地方法栈结构上和Java虚拟机栈一样,只不过Java虚拟机栈是运行Java方法的区域,而本地方法栈是运行本地方法的内存模型。运行本地方法时也会创建栈帧,同样栈帧里也有局部变量表、操作数栈、动态链接和方法返回地址等,在本地方法执行结束后栈帧也会出栈并释放内存资源,也会发生OutOfMemoryError。

最后是程序计数器,程序计数器是一个比较小的内存空间,用来记录当前线程正在执行的那一条字节码指令的地址。如果当前线程正在执行的是本地方法,那么此时程序计数器为空。程序计数器有两个作用,1、字节码解释器通过改变程序计数器来一次读取指令,从而实现代码的流程控制,比如我们常见的顺序、循环、选择、异常处理等。2、在多线程的情况下,程序计数器用来记录当前线程执行的位置,当线程切换回来的时候仍然可以知道该线程上次执行到了哪里。而且程序计数器是唯一一个不会出现OutOfMeroryError的内存区域。

 

方法区和堆都是线程共享的,在JVM启动时创建,在JVM停止时销毁,而Java虚拟机栈、本地方法栈、程序计数器是线程私有的,随线程的创建而创建,随线程的结束而死亡。

1、 String s = new String(“abc”) ; 创建了多上个对象?
答:创建了2个对象,会在编译期创建一个"abc"保存到对象池(常量池)
运行期的出现了new关键字的时候会将对象池中的"abc"复制到堆内存中保存。
2、String s1 = new String(“abc”) ;String s2 = new String(“abc”) ; 创建了多少个对象?
答:创建了三个对象,编译器会在对象池中创建"abc",在运行期会将对象池中的"abc"对象复制两个到堆区。
3、  String  str=“abc”;String  str1=new String(“abc”),一共创建了几个对象呢?
答:创建了两个对象,编译器创建在对象池中创建一个"abc",运行时候会将对象池中的"abc"复制一份到堆中。
String  str1 = new String(“abc”) 和String  str2=“abc”;

7.   Java 堆的结构是什么样子的?
8.   简述各个版本内存区域的变化?
9.   说说各个区域的作用?
10.                Java 中会存在内存泄漏吗,简述一下?
11.                Java 类加载过程?
12.                什么是GC? 为什么要有 GC?
13.                简述一下Java 垃圾回收机制?
14.                如何判断一个对象是否存活?
15.                垃圾回收的优点和原理,并考虑 2 种回收机制?基本原理是什么?
16.                深拷贝和浅拷贝?
17.                什么是分布式垃圾回收(DGC)?它是如何工作的?
18.                在 Java 中,对象什么时候可以被垃圾回收?
19.                简述Minor GC 和 Major GC?
20.                Java 中垃圾收集的方法有哪些?
21.                讲讲你理解的性能评价及测试指标?
22.                常用的性能优化方式有哪些?
23.                说说分布式缓存和一致性哈希?
24.                什么是GC调优?

对象优先在Eden分配
大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC。但也有一种情况,在内存担保机制下,无法安置的对象会直接进到老年代。

大对象直接进入老年代
大对象是指需要大量连续内存空间的Java对象,最典型的大对象就是那种很长的字符串以及数组。
虚拟机提供了一个-XX:PretenureSizeThreshold参数,令大于这个设置值的对象直接在老年代分配。目的就是避免在Eden区及两个Survivor区之间发生大量的内存复制。

长期存活的对象将进入老年代
虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并且对象年龄设为1 。对象在Survivor区中每经过一次Minor GC,年龄就加1岁,当年龄达到15岁(默认值),就会被晋升到老年代中。

满足如下条件之一,对象能晋升老年代:
1.对象的年龄达到了MaxTenuringThreshold(默认15)能晋升老年代。
2.如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。

面试题:什么条件下会触发Minor GC  和Full  GC ?
答:当创建对象的时候Eden的空间不足则触发MinorGC,Minor GC 非常频繁,一般回收速度也比较快,Survivor满不会引发MinorGC。当老年代空间不足的时候会触发FullGC,FullGC 会同时将老年代和新生代(年轻代)的垃圾进行回收。Full GC叫做MajorGC。

Minor GC触发条件:当Eden区满时,触发Minor GC。
Full GC触发条件:
(1)调用System.gc时,系统建议执行Full GC,但是不必然执行
(2)老年代空间不足
(3)方法区(永生代  jdk1.8已经去掉这个概念)空间不足
(4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存
(5)由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

Major GC 是清理OldGen。 Full GC 是清理整个堆空间—包括年轻代和永久代以及老年代OldGen。


Full GC太过频繁如何处理?

Full GC本身是好的,可以清除老年代的垃圾,但是如果Full GC发生的频率高了,就会影响性能,同时意味着系统内存分配机制出现问题。
因为Full GC本身执行时间较长(甚至超过1秒),而且除非采用G1 GC,否则其它的GC方式都会或多或少挂起所有线程执行(Stop-the-world),如果Full GC频繁发生,系统被挂起的次数就会增加,响应时间就会变慢。
同时,Full GC频繁发生,意味着你的内存分配机制存在问题,也许是内存泄露,有大量内存垃圾不断在老年代产生;也许是你的大对象(缓存)过多;也有可能是你的参数设置不好,minor GC清理不掉内存,导致每次minor GC都会触发Full GC;还有可能是你的老年代大小参数设置错误,老年代过小等等原因。也许是服务器问题,需要运维或者客服支撑;也许是数据库查询出现大数据量导致;

老年代与新生代不同,老年代对象存活的时间比较长、比较稳定,因此采用标记(Mark)算法来进行回收,所谓标记就是扫描出存活的对象,然后再进行回收未被标记的对象,回收后对用空出的空间要么进行合并、要么标记出来便于下次进行分配,总之目的就是要减少内存碎片带来的效率损耗。
在执行机制上JVM提供了串行GC(Serial MSC)、并行GC(Parallel MSC)和并发GC(CMS)

 

标记清除:

标记整理:

复制算法:

JVM回收算法小结:

  • 标记清除速度快,但是会产生内存碎片;
  • 标记整理解决了标记清除内存碎片的问题,但是每次都得移动对象,因此成本很高;
  • 复制算法没有内存碎片也不需要移动对象,但是导致空间的浪费;

 

  • 11
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值