JVM00_面试官对类加载器子系统运行时数据区,你能坚持到第几问?
Posted TZ845195485
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM00_面试官对类加载器子系统运行时数据区,你能坚持到第几问?相关的知识,希望对你有一定的参考价值。
因为热爱所以坚持,因为热爱所以等待。熬过漫长无戏可演的日子,终于换来了人生的春天。他逐渐被人熟知,被人喜爱
三年前,在苏州园区被面试官开口就是JVM问懵,根据知识点反复总结的面试问题 待更新
文章目录
- ①. JDK中包含了哪些内容?
- ②. 三大商业虚拟机
- ③. 谈谈你对JVM整体的理解
- ④. 简述Java类加载机制(Java类加载过程)
- ⑤. 什么是类的加载、链接、初始化
- ⑥. 类的主动使用和被动使用
- ⑦. forName("Java.lang.String")和loadClass("Java.lang.String")有什么区别?
- ⑧. 判定一个常量是否"废弃”还是相对简单,而要判定一个类型是否属于"不再被使用的类”的条件就比较苛刻了。需要同时满足下面三个条件
- ⑨. 说说类的加载器分类?
- ⑩. 说说你对引导类加载器、扩展类加载器和应用程序类加载器的理解
- ⑩①. 谈谈你对双亲委派机制的理解
- ⑩②. 双亲委派机制的优势
- ⑩③. 双亲委托模式的弊端
- ⑩④. 双亲委派机制可以打破吗?为什么
- ⑩⑤. 沙箱安全机制谈谈你的认识
- ⑩⑥. 谈谈你对ClassLaoder中的loadClass的理解
- ⑩⑦. Java中基本数据类型存储在JVM中的存储位置
- ⑩⑧. 字节码文件中都有什么?
- ⑩⑨. 字节码文件中都有什么?
- ②0.JVM的永久代中会发生垃圾回收么?
- ②①. JVM在加载类的时候,是否按照类的加载、链接(验证、准备、解析)和初始化的顺序执行的?
- ②②."父加载器"和"子加载器"之间的关系是继承的吗?
- ②③. 如果我们自定义加载器,没有使用双亲委派机制,那么Java中的核心类库是不是还是会进行加载?
- ②④. 为什么JDBC需要打破双亲委派机制?(tomcat也打破了)
- ②⑤. 既然Tomcat不遵循双亲委派机制,那么如果我自己定义一个恶意的HashMap,会不会有风险呢
- ②⑥. 我们思考一下:Tomcat是个web容器,那么它要解决什么问题? (1). 一个web容器可能需要部署两个应用程序,不同的应用程序可能会依赖同一个第三方类库的不同版本,不能要求同一个类库在同一个服务器只有一份,因此要保证每个应用程序的类库都是独立的,保证相互隔离。
- ②⑦. 如果tomcat的CommonClassLoader想加载WebAppClassLoader中的类,该怎么办?
- ②⑧. 为什么java文件放在Eclipse/IDEA中的src文件夹下会优先jar包中的class
- ②⑨. 谈谈你对程序计数器的理解
- ③0. 为什么使用PC寄存器记录当前线程的执行地址呢?
- ③①. PC寄存器为什么设定为线程私有?
- ③①. 说说你对本地方法栈的理解
- ③①. 什么虚拟机栈?
- ③②. 栈里面存在GC吗?
- ③③. 静态变量和局部变量的对比?
- ③④. 虚拟机栈中都有什么?
- ③⑤. 谈谈你对局部变量表的理解?
- ③⑥. 谈谈你对操作数栈的理解?
- ③⑦. 说说你对动态链接的理解?
- ③⑧. 方法返回地址?
- ③⑨. 附加信息(了解)
- ④0. 调整栈大小,就能保证不出现溢出吗?
- ④①. 分配的栈内存越大越好吗?
- ④②. 垃圾回收是否会涉及到虚拟机栈?
- ④③. 方法中定义的局部变量是否线程安全?
- ④④. 什么情况下会发生栈内存溢出?
- ④⑤. 说说堆和栈的区别?
- ④⑥. 简单谈谈你对堆的理解?(共享|垃圾回收)
- ④⑦. 堆里面的分区:Eden,survival(from+to),老年代,各自的特点
- ④⑧. 堆大小通过什么参数设置?
- ④⑨. 初始堆大小和最大堆大小一样,问这样有什么好处?
- ⑤0. 对象在堆中的分配规则?
- ⑤①. Minor GC | Major GC | Full GC
- ⑤②. 什么时候会触发minor gc?
- ⑤③. 是不是所有对象都分配在堆中?
- ⑤④. 方法区中都存放什么东西?
- ⑤⑤. 方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内存溢出错误?
- ⑤⑥. 谈谈你对方法区中字符串常量池、静态变量的变化?
- ⑤⑦. 为什么要用元空间取代永久代
- ⑤⑧. StringTable为什么要调整?
①. JDK中包含了哪些内容?
-
①. JDK是提供给Java开发人员使用的,其中包含了java的开发工具,也包括了JRE。所以安装了JDK,就不用在单独安装JRE了。其中的开发工具:编译工具(javac.exe) 打包工具(jar.exe)等
-
②. JRE(Java Runtime Environment Java运行环境):包括Java虚拟机(JVM Java Virtual Machine)和Java程序所需的核心类库等,如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可
②. 三大商业虚拟机
-
①. sun公司的HotSpot VM(后来被Oracle收购)
-
②. BEA JRocket VM(后来被Oracle收购),JRocket虚拟机中没有解释器,全部代码都靠即时编译器编译后执行
-
③. iBM J9
③. 谈谈你对JVM整体的理解
-
①. 类加载器子系统
-
②. 运行时数据区[我们核心关注这里 的栈、堆、方法区]
-
③. 执行引擎(解释器和JIT编译器共存)
④. 简述Java类加载机制(Java类加载过程)
- 加载机制是指类的加载、链接、初始化的过程
⑤. 什么是类的加载、链接、初始化
-
①. 加载: 将字节码文件中的.class文件,通过类加载器,加载进运行时数据区的方法区内,并创建一个大的Class对象
-
②. 链接:(验证、准备、解析)
- 验证(比如说验证字节码文件开头是CAFFBABA,版本号等)
- 准备(为类变量赋予默认的初始化值,使用static+final修饰,且显示赋值不涉及到方法或者构造器调用的基本数据类型或者String类型的显示赋值都在准备阶段)
- 解析:将类中的符号引用变成直接引用(符号引号在字节码文件的常量池中)
- ③. 初始化:为类变量赋予正确的初始化值,执行Clinit方法(静态代码块或使用static修饰的变量)
注意:一个类中声明类变量,但是没有明确使用类变量的初始化语句以及静态代码块来执行初始化操作时
⑥. 类的主动使用和被动使用
-
①. 当创建一个类的实例时,比如使用new关键字,或者通过反射、克隆、反序列化
-
②. 访问某个类或接口的静态变量,或者对该静态变量赋值
-
③. 调用类的静态方法
-
④. 反射(比如:Class.forName(“com.xiaozhi.Test”))
-
⑤. 初始化一个子类(当初始化子类时,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化)
-
⑥. 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类
-
⑦. JDK7开始提供的动态语言支持 (涉及解析REF_getStatic、REF_putStatic、REF_invokeSt atic方法句柄对应的类)
⑦. forName(“Java.lang.String”)和loadClass(“Java.lang.String”)有什么区别?
-
①. forName()会导致类的主动加载,而getClassLoader()不会导致类的主动加载,Class.forName():是一个静态方法,最常用的是Class.forName(String className);根据传入的类的全限定名返回一个Class对象。该方法在将Class文件加载到内存的同时,会执行类的初始化
-
②. ClassLoader.loadClass():这是一个实例方法,需要一个 ClassLoader 对象来调用该方法。该方法将Class文件加载到内存时,并不会执行类的初始化,直到这个类第一次使用时才进行初始化。(该方法因为需要得到一个ClassLoader 对象,所以可以根据需要指定使用哪个类加载器)
⑧. 判定一个常量是否"废弃”还是相对简单,而要判定一个类型是否属于"不再被使用的类”的条件就比较苛刻了。需要同时满足下面三个条件
-
①. 该类所有的实例都已经被回收。也就是Java堆中不存在该类及其任何派生子类的实例
-
②. 加载该类的类加载器已经被回收。这个条件除非是精心设计的可替换类加载器的场景。如JSP重加载等。否则通常是很难达成的
-
③. 该类对应的Java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法
⑨. 说说类的加载器分类?
-
①. JVM支持两种类型的类加载器,分别为引导类加载器(Bootstrap ClassLoader)和自定义类加载器(User-Defined ClassLoader)
-
②. 从概念上来讲,自定义类加载器一般指的是程序中由开发人员自定义的一类类加载器,但是Java虚拟机规范并没有这么定义,而是将所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器
⑩. 说说你对引导类加载器、扩展类加载器和应用程序类加载器的理解
- ①. 引导类加载器
- 这个类加载使用C/C++语言实现的,嵌套在JVM内部
- 它用来加载Java的核心类库(JAVA_HOME/jre/lib/rt.jar、resource.jar或sum.boot.class.path路径下的内容),用于提供JVM自身需要的类(String类就是使用的这个类加载器)
- 由于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类
- 不继承自java.lang.ClassLoader,没有父加载器
- ②. 扩展类加载器 Extension
- Java语言编写,由sum.music.Launcher$ExtClassLoader实现
- 派生于ClassLoader类,父类加载器为启动类加载器
- 从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录(扩展目录)下加载类库。如果用户创建的JAR放在此目录下,也会自动由扩展类加载器加载
- ③. 系统类加载器(Application)
- Java语言编写,由sum.music.Launcher$AppClassLoader实现
- 派生于ClassLoader类,父类加载器为扩展类加载器
- 它负责加载环境变量classpath或系统属性java.class.path指定路径下的类库
- 调用System.getSystemClassLoader() | Thread.currentThread().getContextClassLoader()获取到的是系统类加载器
⑩①. 谈谈你对双亲委派机制的理解
-
①. 如果一个类加载收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类加载器去执行
-
②. 如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器
-
③. 如果父类的加载器可以完成类的加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式
⑩②. 双亲委派机制的优势
-
①. 避免类的重复加载,确保一个类的全局唯一性(当父ClassLoader已经加载了该类的时候,就没有必要子ClassLoader再加载一次)
比如:我们如果是引导类加载器加载了,就没必要再一次使用扩展类加载器进行加载 -
②. 保护程序安全,防止核心API被随意篡改
⑩③. 双亲委托模式的弊端
- 检查类是否加载的委托过程是单向的,这个方式虽然从结构上说比较清晰,使各个ClassLoader的职责非常明确,但是同时会带来一个问题,即顶层的ClassLoader无法访问底层的ClassLoader所加载的类)
⑩④. 双亲委派机制可以打破吗?为什么
-
①. 双亲委派模型的第一次"被破坏"其实发生在双亲委派模型出现之前——即JDK 1.2面世以前的"远古"时代
-
②. 第二次破坏双亲委派机制:线程上下文类加载器(ClassLoader.getSystemClassLoader( ))
-
③. 双亲委派模型的第三次"被破坏"是由于用户对程序动态性的追求而导致的。如:代码热替换(Hot Swap)、模块热部署(Hot Deployment)
⑩⑤. 沙箱安全机制谈谈你的认识
- ①. 自定义String类,但是在加载String类的时候会使用引导类加载器进行加载,而引导类加载器在加载过程中会先加载jdk自带的文件(rt.jar包中的java\\lang\\String.class),报错信息说没有main方法就是因为加载的是rt.jar包中的String类。这样可以保证对java核心源代码的保护,这就是沙箱安全机制在一定程度上可以保护程序安全,保护原生的JDK代码
⑩⑥. 谈谈你对ClassLaoder中的loadClass的理解
-
①. 先在当前加载器的缓存中查找有无目标类,如果有,直接返回。
-
②. 判断当前加载器的父加载器是否为空,如果不为空,则调用parent.loadClass(name, false)接口进行加载
-
③. 反之,如果当前加载器的父类加载器为空,则调用findBootstrapClassOrNull(name)接口,让引导类加载器进行加载
-
④. 如果通过以上3条路径都没能成功加载,则调用findClass(name)接口进行加载。该接口最终会调用java.lan g.ClassLoader接口的defineClass系列的native接口加载目标Java类
⑩⑦. Java中基本数据类型存储在JVM中的存储位置
- 局部变量存储在栈中,实例变量和静态变量存储在堆中
⑩⑧. 字节码文件中都有什么?
- ①. 魔术、版本(副版本、主版本)、常量池(字节码文件的基石)、访问标识、类索引|父类索引|接口索引集合、字段表集合、方法表集合、属性表集合
⑩⑨. 字节码文件中都有什么?
-
①. 字面量和符号引号
-
②. 字面量:Java中的常量和字符串
-
③. 符号引号:
类和接口的全限定名
属性的名称和修饰符
方法的名称和修饰符
②0.JVM的永久代中会发生垃圾回收么?
-
①. 方法区中的垃圾回收主要是:废弃的常量和不再使用的类型
-
②. HotSpot虚拟机对常量池的回收策略是很明确的,只要常量池中的常量没有被任何地方引用,就可以被回收
-
③. 判定一个常量是否"废弃”还是相对简单,而要判定一个类型是否属于"不再被使用的类”的条件就比较苛刻
了。需要同时满足下面三个条件:
- 该类所有的实例都已经被回收。也就是Java堆中不存在类以及任何派生子类的实例
- 加载该类的加载器已经被回收。这个条件除非是经过设计的可替换类加载器的场景,入OSGI、JSP的重加载等,否则通常是很难达成的
- 该类对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法
②①. JVM在加载类的时候,是否按照类的加载、链接(验证、准备、解析)和初始化的顺序执行的?
- 不过Java虚拟机规范并没有明确要求解析阶段一定要按照顺序执行。在HotSpot VM中,加载、验证、准备和初始化会按照顺序有条不紊地执行,但链接阶段中的解析操作往往会伴随着JVM在执行完初始化之后再执行
②②."父加载器"和"子加载器"之间的关系是继承的吗?
- 不是继承的关系,是包含的关系
②③. 如果我们自定义加载器,没有使用双亲委派机制,那么Java中的核心类库是不是还是会进行加载?
- 会由引导类加载器进行加载,这是因为不管是自定义加载器还是系统类加载器或者扩展类加载器,最终都必须调用java.lang.ClassLoader.defineClass(String,byte[],int,int,ProtectionDomain)方法,而该方法会执行preDefineC lass()接口,该接口中提供给了对JDK核心类库的保护
②④. 为什么JDBC需要打破双亲委派机制?(tomcat也打破了)
- JDBC的Driver接口定义在JDK中,其实现由各个数据库的服务商来提供,比如mysql驱动包,DriverManager类中要加载各个实现了Driver接口的类,然后进行管理,也就是说BootStrap类加载器还要去加载jar包中的Driver接口的实现类,这就打破了双亲委派机制
②⑤. 既然Tomcat不遵循双亲委派机制,那么如果我自己定义一个恶意的HashMap,会不会有风险呢
- 显然不会有风险,如果有,Tomcat都运行这么多年了,那能不改进吗?tomcat不遵循双亲委派机制,只是自定义的classLoader顺序不同,但顶层还是相同的,还是要去顶层请求classloader
②⑥. 我们思考一下:Tomcat是个web容器,那么它要解决什么问题? (1). 一个web容器可能需要部署两个应用程序,不同的应用程序可能会依赖同一个第三方类库的不同版本,不能要求同一个类库在同一个服务器只有一份,因此要保证每个应用程序的类库都是独立的,保证相互隔离。
-
①. 如果使用默认的类加载器机制,那么是无法加载两个相同类库的不同版本的,默认的类加器是不管你是什么版本的,只在乎你的全限定类名,并且只有一份
-
②. 部署在同一个web容器中相同的类库相同的版本可以共享。否则,如果服务器有10个应用程序,那么要有10份相同的类库加载进虚拟机,这是扯淡的。
默认的类加载器是能够实现的,因为他的职责就是保证唯一性。 -
③. web容器也有自己依赖的类库,不能于应用程序的类库混淆。基于安全考虑,应该让容器的类库和程序的类库隔离开来
第三个问题和第一个问题一样 -
④. web容器要支持jsp的修改,我们知道,jsp文件最终也是要编译成class文件才能在虚拟机中运行,但程序运行后修改jsp已经是司空见惯的事情,否则要你何用?所以,web容器需要支持jsp修改后不用重启
我们想我们要怎么实现jsp文件的热替换,jsp文件其实也就是class文件,那么如果修改了,但类名还是一样,类加载器会直接取方法区中已经存在的,修改后的jsp是不会重新加载的。那么怎么办呢?我们可以直接卸载掉这jsp文件的类加载器,所以你应该想到了,每个jsp文件对应一个唯一的类加载器,当一个jsp文件修改了,就直接卸载这个jsp类加载器。重新创建类加载器,重新加载jsp文件
②⑦. 如果tomcat的CommonClassLoader想加载WebAppClassLoader中的类,该怎么办?
- 看了前面的关于破坏双亲委派模型的内容,我们心里有数了,我们可以使用线程上下文类加载器实现,使用线程上下文加载器,可以让父类加载器请求子类加载器去完成类加载的动作
②⑧. 为什么java文件放在Eclipse/IDEA中的src文件夹下会优先jar包中的class
- Tomcat类加载机制的理解,就不难明白。因为Eclipse/IDEA中的src文件夹中的文件java以及webContent中的JSP都会在tomcat启动时,被编译成class文件放在WEB-INF/class中。
而Eclipse/IDEA外部引用的jar包,则相当于放在WEB-INF/lib中。
因此肯定是java文件或者JSP文件编译出的class优先加载。
②⑨. 谈谈你对程序计数器的理解
-
①. 作用:是用来存储指向下一条指令的地址,也即将要执行的指令代码。由执行引擎读取下一条指令
-
②. 是线程私有的 、不会存在内存溢出(唯一一个运行时数据区没有OOM的区域)
-
③. 如果执行的是一个Native方法,那这个计数器是undefined
③0. 为什么使用PC寄存器记录当前线程的执行地址呢?
-
①. 因为CPU需要不停的切换各个线程,这时候切换回来以后,就得知道接着从哪开始继续执行
-
②. JVM的字节码解释器就需要通过改变PC寄存器的值来明确下一条应该执行什么样的字节码指令
③①. PC寄存器为什么设定为线程私有?
- 为了能够准确记录各个线程正在执行的当前字节码指令地址,最好的办法自然是为每一个线程都分配一个PC寄存器
③①. 说说你对本地方法栈的理解
-
①.本地接口的作用是融合不同的编程语言为Java所用,它的初衷是融合C/C++程序,Java诞生的时候是C/C++横行的时候,要想立足,必须由调用C/C++程序,于是就在内存中专门开辟了一块区域处理标记为native的代码,它的具体做法是Native Method Stack中登记native方法,在Execution Engine执行时加载native libraies
-
②. native方法的举例:Object类中的clone wait notify hashCode等Unsafe类都是native方法
③①. 什么虚拟机栈?
- ①.虚拟机栈(Java Virtual Machine Stacks)线程是紧密联系的,每创建一个线程时就会对应创建一个Java栈, 所以Java栈也是"线程私有"的内存区域,这个栈中又会对应包含多个栈帧,每调用一个方法时就会往栈中创建并压入一个栈帧,栈帧是用来存储方法数据和部分过程结果的数据结构,每一个方法从调用到最终返回结果的过程,就对应一个栈帧从入栈到出栈的过程 [先进后出]
③②. 栈里面存在GC吗?
- 栈中是不存在GC的,存在OOM和StackOverflowError
③③. 静态变量和局部变量的对比?
-
①. 我们知道类变量表有两次初始化的机会,第一次是在"准备阶段",执行系统初始化,对类变量设置为零值,另一次则是在"初始化"阶段,赋予程序员在代码中定义的初始值
-
②. 和类变量初始化不同的是,局部变量表不存在初始化的过程,这意味着一旦定义了局部变量则必须认为初始化
③④. 虚拟机栈中都有什么?
- 局部变量表、操作数栈、动态链接、方法的返回地址、附件信息
③⑤. 谈谈你对局部变量表的理解?
-
①. 定义为一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量这些数据类型包括各种基本数据类型、对象引用(reference)以及return Address类型)
-
②. 由于局部变量是建立在线程的栈上,是线程私有数据,因此不存在数据安全问题
-
③. 局部变量表所需容量大小是在编译期确定下来的。(并保存在方法Code属性的maximum local variables数据项中,在方法运行期间不会改变局部变量表的大小的)
-
④. 局部变量表,是基本的存储单元是slot(变量槽)
-
⑤. 在局部变量表中,32位以内的类型只占有一个slot(包括引用数据类型),64位的类型(long和double)占有两个slot、byte、short、char在存储前被转换为int,boolean也被转换为int(0表示fasle,非0表示true)long和double则占据两个slot
-
⑥. Jvm会为局部变量表中的每一个slot都分配一个访问索引,通过这个索引即可成功访问到局部变量表中指定的局部变量值
-
⑦. 如果需要访问局部变量表中一个64bit的局部变量值时,只需要使用前一个索引即可(比如:访问long或double类型变量)
-
⑧. 如果当前帧是由构造方法或者实例方法创建,那么该对象引用this将会放在index为0的slot处
-
⑨. 栈帧中的局部变量表中的槽位是可以复用的,如果一个局部变量过了其作用域,那么在其作用域之后申请的新的局部变量就很可能会复用过期局部变量的槽位,从而节省资源的目的
-
⑩. 局部变量表中的变量也是重要的垃圾回收根节点,只要被局部变量表中直接或间接引用的对象都不会被回收
③⑥. 谈谈你对操作数栈的理解?
-
①. 我们说Java虚拟机的解释引擎是基于栈的执行引擎,其中的栈指的就是操作数栈
-
②. 每一个独立的栈帧中除了包含局部变量表以外,还包含一个后进先出(Last-In-First-Out)的操作数栈,也可以称之为表达式栈
-
③. 每一个操作数栈都会拥有一个明确的栈深度用于存储数值,其所需的最大深度在编译期就定义好了,保存在方法的Code属性中,为max_stack的值
-
④. 栈中的任何一个元素都是可以任意的Java数据类型
32bit的类型占用一个栈单位深度
64bit的类型占用两个栈单位深度 -
⑤. 操作数栈,主要用于保存计算机过程的中间结果,同时作为计算过程中变量临时的存储空间
③⑦. 说说你对动态链接的理解?
- 动态链接就是将字节码文件中的符号引号(字符串常量池中)变成直接引用的过程
③⑧. 方法返回地址?
-
①. 存放调用该方法的PC寄存器的值
-
②. 一个方法的结束,有两种方式:
正常执行完成
出现未处理的异常,非正常退出 -
③. 无论通过哪种方式退出,在方法退出后都返回到该方法被调用的位置。方法正常退出时,调用者的pc计数器的值作为返回地址,即调用该方法的指令的下一条指令的地址。
而通过异常退出的,返回地址是要通过异常表来确定,栈帧中一般不会保存这部分信息。
③⑨. 附加信息(了解)
- 栈帧中还允许携带与Java虚拟机实现相关的一些附加信息。例如,对程序调试提供支持的信息。
④0. 调整栈大小,就能保证不出现溢出吗?
- 不能。因为调整栈大小,只会减少出现溢出的可能,栈大小不是可以无限扩大的,所以不能保证不出现溢出
④①. 分配的栈内存越大越好吗?
- 不是,因为增加栈大小,会造成每个线程的栈都变的很大,使得一定的栈空间下,能创建的线程数量会变小
④②. 垃圾回收是否会涉及到虚拟机栈?
-
①. 不会;垃圾回收只会涉及到方法区和堆中,方法区和堆也会存在溢出的可能
-
②. 程序计数器,只记录运行下一行的地址,不存在溢出和垃圾回收
-
③. 虚拟机栈和本地方法栈,都是只涉及压栈和出栈,可能存在栈溢出,不存在垃圾回收
④③. 方法中定义的局部变量是否线程安全?
- 如果局部变量在内部产生并在内部消亡的,那就是线程安全的
④④. 什么情况下会发生栈内存溢出?
-
①. 局部数组过大。当函数内部的数组过大时,有可能导致堆栈溢出
-
②. 递归调用层次太多。递归函数在运行时会执行压栈操作,当压栈次数太多时,也会导致堆栈溢出
④⑤. 说说堆和栈的区别?
-
①.从GC、OOM、StackOverflowError的角度
[栈中不存在GC,当固定大小的栈会发生OOM,动态的会发生StackOverflowError。堆中GC、OOM、StackOverflowError都存在] -
②. 从堆栈的执行效率[栈的效率高于堆]
-
③. 内存大小,数据结构
[堆的空间比栈的大一般,栈是一种FIFO先进后出的模型。堆中结构复杂,可以有链表、数组等] -
④. 栈管运行,堆管存储
④⑥. 简单谈谈你对堆的理解?(共享|垃圾回收)
-
①. Java堆区在JVM启动的时候即被创建,其空间大小也是确定的。是Jvm管理最大的一块内存空间
-
②. 所有的线程共享Java堆,在这里还可以划分线程私有的缓冲区(Thread Local Allocation Buffer,TLAB)
-
③. 在方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才有被移除
(注意:一个进程就是一个JVM实例,一个进程中包含多个线程) -
④. 所有的线程共享Java堆,在这里还可以划分线程私有的缓冲区(Thread Local Allocation Buffer, TLAB)
④⑦. 堆里面的分区:Eden,survival(from+to),老年代,各自的特点
-
①. 在JDK1.7中分为: 新生代+老年代+永久代 | 在JDK1.8中分为: 新生代+老年代+元空间
-
②. 新生代:伊甸园区、幸存者S0、S1(8:1:1),几乎所有的Java对象都是在Eden区被new出来的,
绝大部分的Java对象的销毁都在新生代进行了;IBM公司的专门研究表明,新生代中 80% 的对象都是"朝生夕死"的
老年代:另外一类对象的生命周期却非常长,在某些极端的情况下还能够与JVM的生命周期保持一致
新生代:老年代=1:2
④⑧. 堆大小通过什么参数设置?
-
①. -Xms:初始内存(默认为物理内存的1/64)
-
②. -Xmx:最大内存(默认为物理内存的1/4)
-
③. -XX:NewRatio=2
-XX:SurvivorRatio
-XX:HandlePromotionFailure:空间分配担保
-Xmn:设置新生代最大内存大小,一般使用默认值就可以了
④⑨. 初始堆大小和最大堆大小一样,问这样有什么好处?
- 通常会将-Xms和-Xmx两个参数配置相同的值,其目的是为了能够在java垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小,从而提升性能
⑤0. 对象在堆中的分配规则?
-
①. 优先分配到Eden
-
②. 大对象直接分配到老年(尽量避免程序中出现过多的大对象)
-
③. 长期存活的对象分配到老年代
-
④. 动态对象年龄判断(如果Survivor 区中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄对象可以直接进入老年代,无须等到MaxTenurningThreshold中要求的年龄
-
⑤. 空间分配担保 -XX:HandlePromotionFailure
(JDK6 Update24之后规则变为只有老年代的连续空间大于新生代对象总大小或者历次晋身的平均大小就会进行Minor GC,否则将进行Full GC)
⑤①. Minor GC | Major GC | Full GC
-
①. Minor GC 在Eden伊甸园区满的时候会触发,发生在新生代中
-
②. Major GC 在老年代中满了会进行触发,发生在老年代,major gc的时间比minor gc时间长
-
③. Full GC 发生在整个堆中
⑤②. 什么时候会触发minor gc?
- 在Eden伊甸园区满的时候会触发
⑤③. 是不是所有对象都分配在堆中?
- 几乎所有对象是分配在堆中,如果一个对象在方法中没有进行逃逸,也可以分配在栈中进行分配
⑤④. 方法区中都存放什么东西?
- 类型信息、常量、静态变量、即时编译器编译后的代码缓存
⑤⑤. 方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内存溢出错误?
-
①. 加载大量的第三方的jar包
-
②. tomcat部署的工程过多(30-50个)
-
③. 大量动态的生成反射类
⑤⑥. 谈谈你对方法区中字符串常量池、静态变量的变化?
-
①. Jdk 1.6 及之前:有永久代,静态变量、字符串常量池1.6在方法区
-
②. Jdk 1.7 :有永久代,但已经逐步 " 去永久代 ",字符串常量池、静态变量移除,保存在堆中
-
③. jdk 1.8 及之后:无永久代,常量池1.8在元空间。但静态变量、字符串常量池仍在堆中
⑤⑦. 为什么要用元空间取代永久代
-
①.永久代参数设置过小,在某些场景下,如果动态加载的类过多,容易产生Perm区的OOM,比如某个实际Web工程中,因为功能点比较多,在运行过程中,要不断动态加载很多类,经常出现致命错误
-
②.永久代参数设置过大,导致空间浪费
-
③. 默认情况下,元空间的大小受本地内存限制)
-
④. 对永久代进行调优是很困难的
[方法区的垃圾收集主要回收两部分:常量池中废弃的常量和不再使用的类型,而不再使用的类或类的加载器回收比较复杂,full gc 的时间长]
⑤⑧. StringTable为什么要调整?
-
①.jdk7中将StringTable放到了堆空间中。因为永久代的回收效率很低,在full gc的时候才能触发。而full gc是老年代的空间不足、永久代不足才会触发
-
②. 这就导致StringTable回收效率不高,而我们开发中会有大量的字符串被创建,回收效率低,导致永久代内存不足,放到堆里,能及时回收内存
以上是关于JVM00_面试官对类加载器子系统运行时数据区,你能坚持到第几问?的主要内容,如果未能解决你的问题,请参考以下文章