知晓JVM系列(四): JVM相关问题问答(面试题)

本文深入探讨Java内存管理机制,详细解释堆、栈、常量池、方法区等概念,阐述垃圾回收机制原理及如何避免内存泄露、溢出问题。同时,介绍JVM的运行时环境、内存分配与回收策略,以及多种垃圾收集器的特点和区别,旨在帮助开发者理解和优化内存使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.问:堆和栈有什么区别

       答:堆是存放对象的,但是对象内的临时变量是存在栈内存中,如例子中的methodVar是在运行期存放到栈中的。

      栈是跟随线程的,有线程就有栈,堆是跟随JVM的,有JVM就有堆内存。

2.问:堆内存中到底存在着什么东西?

       答:对象,包括对象变量以及对象方法。

3.问:类变量和实例变量有什么区别?

       答:静态变量是类变量,非静态变量是实例变量,直白的说,有 static修饰的变量是静态变量,没有 static修饰的变量是实例变量。静态变量
       存在方法区中,实例变量存在堆内存中。

4.问:我听说类变量是在JVM启动时就初始化好的,和你这说的不同呀!

       答:那你是道听途说,信我的,没错。

5.问:Java的方法(函数)到底是传值还是传址?

       答:都不是,是以传值的方式传递地址,具体的说原生数据类型传递的值,引用类型传递的地址。对于原始数据类型, JVM的处理方法是       
       从 Method AreaHeap中拷贝到 Stack,然后运行 frame中的方法,运行完毕后再把变量指拷贝回去。

6.问:为什么会产生OutOfMemory产生?

      答:一句话: Heap内存中没有足够的可用内存了。这句话要好好理解,不是说 Heap没有内存了,是说新申请内存的对象大于 Heap空闲内 
      存,比如现在 Heap还空闲 1M,但是新申请的内存需要 1.1M,于是就会报 OutOfMemory了,可能以后的对象申请的内存都只要 0.9M,于 
      是就只出现一次 OutOfMemoryGC也正常了,看起来像偶发事件,就是这么回事。        但如果此时 GC没有回收就会产生挂起情况,系统
      不响应了。

7.问:我产生的对象不多呀,为什么还会产生OutOfMemory

        答:你继承层次忒多了, Heap中 产生的对象是先产生 父类,然后才产生子类,明白不?

8.问:OutOfMemory错误分几种?

       答:分两种,分别是“ OutOfMemoryError:java heap size OutOfMemoryError: PermGen space ,两种都是内存溢出, heap  
       size是说申请不到新的内存了,这个很常见,检查应用或调整堆内存大小。

    “ PermGen space 是因为永久存储区满了,这个也很常见,一般在热发布的环境中出现,是因为每次发布应用系统都不重启,久而久之永久 
      存储区中的死对象太多导致新对象无法申请内存,一般重新启动一下即可。

9.问:为什么会产生StackOverflowError

      答:因为一个线程把Stack内存全部耗尽了,一般是递归函数造成的。

10.问:一个机器上可以看多个JVM吗?JVM之间可以互访吗?

       答:可以多个 JVM,只要机器承受得了。 JVM之间是不可以互访,你不能在 A-JVM中访问 B-JVMHeap内存,这是不可能的。在以前老版
      本的 JVM中,会出现 A-JVM Crack后影响到 B-JVM,现在版本非常少见。

11.问:为什么Java要采用垃圾回收机制,而不采用C/C++的显式内存管理?

       答:为了简单,内存管理不是每个程序员都能折腾好的。

12.问:为什么你没有详细介绍垃圾回收机制?

       答:垃圾回收机制每个 JVM都不同, JVM Specification只是定义了要自动释放内存,也就是说它只定义了垃圾回收的抽象方法,具体怎么实
       现各个厂商都不同,算法各异,这东西实在没必要深入。

13.问:JVM中到底哪些区域是共享的?哪些是私有的?

        答:HeapMethod Area是共享的,其他都是私有的,

14.问:什么是JIT,你怎么没说?

       答: JIT是指 Just In Time,有的文档把 JIT作为 JVM的一个部件来介绍,有的是作为执行引擎的一部分来介绍,这都能理解。 Java刚诞生的时
       候是一个解释性语言,别嘘,即使编译成了字节码( byte code)也是针对 JVM的,它需要再次翻译成原生代码 (native code)才能被机器执
       行,于是效率的担忧就提出来了。 Sun为了解决该问题提出了一套新的机制,好,你想编译成原生代码,没问题,我在 JVM上提供一个工
       具,把字节码编译成原生码,下次你来访问的时候直接访问原生码就成了,于是 JIT就诞生了,就这么回事。

15.问:JVM还有哪些部分是你没有提到的?

       答:JVM是一个异常复杂的东西,写一本砖头书都不为过,还有几个要说明的:

        常量池( constant pool):按照顺序存放程序中的常量,并且进行索引编号的区域。比如 int i =100,这个 100就放在常量池中。
        安全管理器( Security Manager):提供 Java运行期的安全控制,防止恶意攻击,比如指定读取文件,写入文件权限,网络访问,创建进程  
        等等, Class LoaderSecurity Manager认证通过后才能加载 class文件的。 
        方法索引表( Methods table ),记录的是每个 method 的地址信息, Stack Heap 中的地址指针其实是指向 Methods table 地址。   

16.问:为什么不建议在程序中显式的生命System.gc()

       答:因为显式声明是做堆内存全扫描,也就是 Full GC,是需要停止所有的活动的( Stop  The World Collection),你的应用能承受这个
        吗?

17.问:JVM有哪些调整参数?

       答:非常多,自己去找,堆内存、栈内存的大小都可以定义,甚至是堆内存的三个部分、新生代的各个比例都能调整。
 
       18.问:Java中会存在内存泄漏吗?
      答: Java中也存在内存泄露。当被分配的对象可达但已无用(未对作废数据内存单元的引用置null)即会引起。
     
       19.问: 内存泄露、溢出的异同?
      答:同:都会导致应用程序运行出现问题,性能下降或挂起。          
             异: 
            1) 内存泄露是导致内存溢出的原因之一;内存泄露积累起来将导致内存溢出。 
            2) 内存泄露可以通过完善代码来避免;内存溢出可以通过调整配置来减少发生频率,但无法彻底避免。

       20.问:如何检测内存泄露?
      答:可以通过一些性能监测分析工具,如 JProfiler、Optimizeit Profiler。
 
       21.问:如何避免内存泄露、溢出? 
       答: 1) 尽早释放无用对象的引用。 
                   好的办法是使用临时变量的时候,让引用变量在退出活动域后自动设置为null,暗示垃圾收集器来收集该对象,防止发生内存泄露。                 
               2) 程序进行字符串处理时,尽量避免使用String,而应使用StringBuffer。
               3) 尽量少用静态变量。 
                  因为静态变量是全局的,GC不会回收。 
               4) 避免集中创建对象尤其是大对象,如果可以的话尽量使用流操作。          JVM会突然需要大量内存,这时会触发GC优化系统内存环
                   境
               5) 尽量运用对象池技术以提高系统性能。 
                   生命周期长的对象拥有生命周期短的对象时容易引发内存泄漏,例如大集合对象拥有大数据量的业务对象的时候,可以考虑分块进行
                    处理,然后解决一块释放一块的策略。 
               6) 不要在经常调用的方法中创建对象,尤其是忌讳在循环中创建对象。 
                   可以适当的使用hashtable,vector 创建一组对象容器,然后从容器中去取那些对象,而不用每次new之后又丢弃。          7) 优化
                   配置。

       22.问:内存溢出的解决方案?  
      答:一是从代码层面进行优化完善,尽量避免该情况发生; 
             二是调整优化服务器配置

       23.问:jvm运行时环境包括哪些?
      答:程序计数器、方法区、堆内存、java栈(本地方法栈、虚拟机栈)

       24.问:内存溢出有几种情况?
      答: 1. java.lang.outOfMemoryError:Java heap space (堆溢出)
           2.java.lang.outOfMemoryError: PermGen space  (方法区溢出)
           3. java.lang.OutOfMemoryError:unable to create new native thread( 创建线程导致内存溢出 )
           4. java.lang.OutOfMemoryError: GC overhead limit exceeded(某项操作使用大量内存时发生)
           5. java.lang.StackOverflowError                           (虚 拟机栈和本地方法栈溢出 )
           6. java.lang.OutOfMemoryError                             (直接内存 溢出 )
        25.问:JVM内存模型?
            答:程序计数器、堆、java栈(本地方法栈、虚拟机栈)、方法区。
       26. 问:写一段代码实现堆溢出?栈溢出?常量池溢出?方法区溢出?
                  参考: http://my.oschina.net/kanlianhui/blog/223347
       27.问:类的生命周期?
             答:加载、链接、初始化( 校验、准备、解析 )、使用(实例化)、卸载
                            http://blog.youkuaiyun.com/ctf_htj/article/details/17687965
                             http://blog.youkuaiyun.com/zhengzhb/article/details/7517213 
       28. 问:哪四种情况会触发类的初始化(也就是主动引用)?
            答: 1. 遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。
                   2. 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
                   3. 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
                   4. 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。
                   参看: http://www.2cto.com/kf/201409/334714.html
       29.问:被动引用会触发初始化吗?请举例
            答:不会。
                  1. 通过子类引用父类的静态字段,不会导致子类初始化
                  2. 通过数组定义来引用类,不会触发该类的初始化
                  3.   常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的那个类,所以也不会触发 
                  定义常量的类的初始化
                  参看: http://www.myexception.cn/program/1750220.html 
                             http://my.oschina.net/sel/blog/214958 
       30. 问:Class对象在堆中的作用其生成方式,主要信息?
             答:Class对象在堆中的作用创建类的常规对象的。
                    参看: http://wk.yl1001.com/doc_8531370677322966_all.htm 
                    生成方式:  1.Class.forName("类名字符串")
                                     2.类名.class
                                     3.实例对象.getClass()
                                     参看: http://developer.51cto.com/art/201202/316177.html 
                   主要信息: (1)class对象的信息;(2)class对象的成员信息
                     参看: http://peiquan.blog.51cto.com/7518552/1298448 
       31.  问:Java类加载机制
             答: 1、寻找jre目录,寻找jvm.dll,并初始化JVM;  
                    2、产生一个Bootstrap Loader(引导类加载器); 
                    3、Bootstrap Loader自动加载Extended Loader(扩展类加载器),并将其父Loader设为Bootstrap Loader。 
                    4、Bootstrap Loader自动加载AppClass Loader(系统类加载器),并将其父Loader设为Extended Loader。 
                    5、最后由AppClass Loader加载HelloWorld类。     
                   参看: http://my.oschina.net/xianggao/blog/70826 
                              http://lavasoft.blog.51cto.com/62575/184547/ 
       32.问:类加载器(ClassLoader)的类加载模式?为什么要选择这种模式?双亲委托加载模式的代码实现?
              答: 委托模式。
                    原因:  1、节约系统资源。只要,这个类已经被加载过了,就不会在次加载。
                            2、保证 Java 核心库的类型安全。所有 Java 应用都至少需要引用  java.lang.Object类,也就是说在运行的时     
                               候, java.lang.Object这个类需要被加载到 Java 虚拟机中。如果这个加载过程由 Java 应用自己的类加载器来完成的 
                               话,很可能就存在多个版本的  java.lang.Object类,而且这些类之间是不兼容的。通过代理模式,对于 Java 核心库的 
                               类的加载工作由引导类加载器来统一完成,保证了 Java 应用所使用的都是同一个版本的 Java 核心库的类,是互相兼容的。
                      双亲委托加载模式:参看: http://blog.youkuaiyun.com/yfqnihao/article/details/8257491 
       33.问: 如何实现自定义的ClassLoader?
                 答:继承ClassLoader
                   参看: http://blog.youkuaiyun.com/hahawen/article/details/409285 
                              http://my.oschina.net/xianggao/blog/70826 
       34.问:JDK动态代理的原理?
            答: jdk的动态代理是基于接口的,必须实现了某一个或多个任意接口才可以被代理,并且只有这些接口中的方法会被代理。 代理类和目标类都 
                  拥有相同的接口,所以它们都拥有相同的行为。 代理类的对象本身并不真正提供服务,而是调用目标类对象的相关方法,来提供 
                  特定的服务。
                  参看:http://blog.youkuaiyun.com/xyang81/article/details/7277750 
                             http://wenku.baidu.com/view/f9d9e0886529647d272852d1.html 
                             http://www.360doc.com/content/14/0831/12/820209_406004862.shtml 
                            
       35.问:虚拟机栈栈帧包含哪些部分以及各部分功能?
            答:局部变量表:   局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量,其中存放的数据的类型是 
                                       编译期可知的各种基本数据类型、对象引用(reference)和returnAddress类型(它指向了一条字节码指令
                                       的地址)。
                  操作数栈: 在方法的执行过程中,会有各种字节码指令(比如:加操作、赋值元算等)向操作栈中写入和提取内容
                   动态链接:  每个栈帧都包含一个指向运行时常量池(在方法区中,后面介绍)中该栈帧所属方法的引用,持有这个引用是
                                    为了支持方法调用过程中的动态连接。
                   方法返回地址: 当一个方法被执行后,有两种方式退出该方法:执行引擎遇到了任意一个方法返回的字节码指令或遇到了异
                                           常,并且该异常没有在方法体内得到处理。
                   参看: http://blog.youkuaiyun.com/ns_code/article/details/17565503 
       36.问: 字节码执行引擎方法调用中静态分派和动态分派区别?重写与重载?
            答:   所有依赖静态类型来定位方法执行版本的分派动作,都称为静态分派,静态分派的最典型应用就是多态性中的方法重载。
                    根据变量的实际类型来分派方法的执行版本的 。而实际类型的确定需要在程序运行时才能确定下来,这种在运行期根据实 
                   际 类型确定方法执行版本的分派过程称为动态分派。 动态分派 是多态性中的方法重写。
                   参看: http://blog.youkuaiyun.com/ns_code/article/details/17965867 
                     重载与覆盖的区别
                  1、方法的覆盖是子类和父类之间的关系,是垂直关系;方法的重载是同一个类中方法之间的关系,是水平关系。
                  2、覆盖只能由一个方法,或只能由一对方法产生关系;方法的重载是多个方法之间的关系。
                  3、覆盖要求参数列表相同;重载要求参数列表不同。
                  4、覆盖关系中,调用那个方法体,是根据对象的类型(对象对应存储空间类型)来决定;重载关系,是根据调用时的实参表与形参
                   表来选择方法体的。
       37.问: JVM为什么选用基于栈的指令集而不是基于二地址指令集?(可移植性)
            答: 基于 stack 的字节码执行引擎, 一般来说指令集有基于寄存器和基于栈的。 JVM 为了和平台无关性,选用了基于栈 
                   的指令集。
       38.问: 动态代理的原理?好处?JDK动态代理与Cglib动态代理的区别?
       39.问: 垃圾收集算法?标记-清除算法缺点?复制算法缺点?标记-整理算法适用区域?解释一下分代收集算法?
             答: :对虚拟机可用内存空间,即堆空间中的对象进行识别,如果对象正在被引用,那么称其为存活对象,反之,如果对象不 
                       再被引用,则为垃圾对象,可以回收其占据的空间,用于再分配。垃圾收集算法的选择和垃圾收集系统参数的合理调节  
                       直接影响着系统性能,因此需要开发人员做比较深入的了解。 
                        标记-清除算法缺点: 标记清除算法是最基础的收集算法,其他收集算法都是基于这种思想。标记清除算法分为“标       
                                                      记”和 清除”两个阶段:首先标记出需要回收的对象,标记完成之后统一 清除对象。

                                                      它的主要缺点:①.标记和清除过程效率不高 。

                                                                               ②.标记清除之后会产生大量不连续的内存碎片。
                        复制算法缺点: 主要缺点: 内存缩小为原来的一半。
                        标记-整理算法适用区域:年轻代
                        分代收集算法: 根据对象的存活周期的不同将内存划分为几块。一般把java堆分为新生代和老年代,这样就可以根据各个年代的 
                       特点采用最适当的收集算法。在新生代,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付
                       出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有 额外空间对他进行分配担保,就必须使用“标
                       记-整理”算法进行回收。
                        参看: http://www.cnblogs.com/zuoxiaolong/p/jvm5.html 
                                   http://my.oschina.net/hanruikai/blog/337866 

       40.问: 内存分配与回收策略?
       41.问: 有哪些垃圾收集器以及各自区别?Serial与ParNew异同点? ParNew与Paralell Scanvenge区别?
       42. 问:  CMS最大特点及显著缺点?
       43. 问: 哪些对象可归为GC Roots?
              答:    1、虚拟机栈中的引用的对象。

                        2、方法区中的类静态属性引用的对象。

                        3、方法区中的常量引用的对象。

                        4、本地方法栈中JNI的引用的对象。

                 第一和第四种都是指的方法的本地变量表,第二种表达的意思比较清晰,第三种主要指的是声明为final的常量值。

                 全局 对象 、静态变量、局部 对象 、函数调用参数、当前CPU寄存器中的 对象 指针(还有 finalizationqueue)
                 参看: http://www.cnblogs.com/zuoxiaolong/p/jvm3.html 
       44.问:  MinorGC与FullGC?
                  答: minor GC :对新生代进行的GC操作。通常采用复制算法,将Eden Space 以及survivor Space 0的不可回收对象复制到
                    survivor Space 1。 
                    full GC :对老年代进行的GC操作。通常采用标记-清除算法,即将不可回收对象做上标记,回收其它未被标记对象,这样做有一
                   个缺点,会产生大量内存碎片。
                     FULL GC一般采用的是标记—整理算法;
                         MINOR GC一般采用的是复制算法;
                    参看: http://www.blogjava.net/ldwblog/archive/2013/07/24/401919.html 
       45.问:   JVM内存管理的机制
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值