栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

jvm面试题

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

jvm面试题

1.jvm的监控工具?

        答:1)jvisualvm图形化工具;2)JConsole:JMX的可视化管理工具

164.Java虚拟机的内存布局?

        答:主要分为:本地方法栈(线程私有),虚拟机栈(线程私有),程序计数器(线程私有),方法区,堆。 1)本地方法栈:是虚拟机为用到的本地方法服务,要是请求的栈深度比较大,会抛出StackOverFlowError和OutOfMemoryError。 2)虚拟机栈:是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口信息。它的生命周期和线程相同。 3)程序计数器:是当前线程执行的字节码的行号指示器,字节码解释器工作时就是通过改变这个计数器的值来选取下一条要执行的字节码服务, 分支,循环,跳转,异常处理,线程不回复等基础功能都需要依赖这个计数器来完成。 4)堆:是所有线程共享的一块内存,主要存放对象实例,堆还分为新生代和老生代;再细分的话有Eden空间,From Survivor空间, To Survivor空间等。该区域也是垃圾回收主要区域。 5)方法区:用于存储已被虚拟机加载的类信息,常量,静态变量,即使编译器编译后的代码等数据。

289、说说Java虚拟机的生命周期及体系结构?

        答:生命周期:当启动一个Java程序时,一个虚拟机实例就诞生了, 当程序关闭退出时,这个虚拟机实例随之消亡。JVM实例通过main()方法来运行一个Java程序。 而这个main()方法必须是共有的(public)、静态的(static)、返回void,并且接收一个字符串数组为参数。 Java程序初始类中的main()方法,将作为改程序初始线程的起点,任何其他线程都是由这个初试线程启动的。 JVM内部有两种线程:守护线程与非守护线程。守护线程通常是由虚拟机自己使用的,比如垃圾回收线程。 当该程序所有的非守护线程都终止时,JVM实例将自动退出。 体系结构:1)类装载器子系统:在JVM中负责查找并装载类型, JVM中有两种类装载器:启动类装载器和用户自定义类装载器; 2)方法区:类中的类(静态)变量同样存储在方法区中。所有线程共享方法区, 因此,它们对方法区的数据访问必须被设计为线程安全的。方法区的大小不必是固定的; 3)堆:同一个Java程序的多个线程共享同一个堆空间,所有线程都将共享这个堆,Java程序独占一个JVM实例; 4)程序计数器:当执行本地方法时,PC寄存器的内容是下一条将被执行指令的地址;当执行本地方法时,PC寄存器中的值是“undefined”; 5)Java栈:每当启动一个新的线程时,JVM都会为它分配一个Java栈,Java栈上的所有数据都是此线程所独有的; 6)本地方法栈:本地方法本质上是依赖于实现的,JVM实现的设计者们可以自己决定使用怎样的方式来让Java程序调用本地方法; 7)执行引擎:在Java虚拟机规范中,执行引擎的行为使用指令集来定义。

35、Java虚拟机中,数据类型可以分为哪几类?

        答:总共有两类:第一类是:原始类型:数值类型(int、short、long、double、char、byte) 和Boolean类型(true、false)与returnAddress类型(是指向某个操作码的指针, 此操作码与Java虚拟机的指令相对应); 第二类是:引用类型:1)类类型(class type):指向动态创建的类实例; 2)数组类型(array type):指向数值实例;3)接口类型(interface type):指向实现了某个接口的类实例或数组实例; 4)在引用类型中还存在一个特殊的值:null,当一个引用不指向任何对象时,它的值就是null,一个为null的引用可以转 换为任意的引用类型,null也就是引用类型的默认值。

125.JVM里的有几种类加载器(classloader),为什么会有多种?

        答:1)启动类加载器,扩展类加载器,应用类加载器,用户自定义类加载器   2)启动类加载器负责加载JRE核心类库,如:JRE下的 rt.jar , charset.jar ,比如String类   3) 扩展类加载器负责加载JRE下的ext目录下的jar包   4)应用类加载器负责加载classpath下的jar包 166.类加载机制,也就是双亲委派模型? 答:类加载机制: 1)加载:通过类的全限定名找到这个类所在的位置,把它转化为虚拟机内存中的对象。 2)验证:检查java文件对应class文件是否符合虚拟机要求。 3)准备:为类变量(static修饰)分配内存,并设置初始值(例如int为0)。 4)解析:就是将常量值的引用替换为实际值的过程。 5)初始化:初始化会把类中所有用了static修饰的变量以及静态语句块执行一遍。 双亲委派模型:不管是应用程序类加载器还是自定义类加载器在进行类加载时它首先不会自己去加载, 它会把加载任务委派给自己的父类加载器,比如有个类需要自定义类加载器来加载,它会把它交给应用程序类加载器, 应用程序类加载器又会把任务交给扩展类加载器,一直往上提交,直到启动类加载器。启动类加载器如果在自己的扫描范围内能找到类, 它就会去加载,如果它找不到,它就会交给它的下一级子加载器去加载,以此类推。

144.深入分析了Classloader,双亲委派机制?

        答:ClassLoader分为根加载器(Bootstrap ClassLoader)和其它的加载器。 其中根加载器负责加载Java的核心类,由JVM实现,而其它类加载器都由Java层实 现并继承java.lang.ClassLoader。1)启动类加载器负责将%JAVA_HOME%/lib目 录中或-Xbootclasspath中参数指定的路径中的,并且是虚拟机识别的(按名称)类库加载到JVM中。 2)Extension ClassLoader(扩展类加载器)负责加载%JAVA_HOME%/lib/ext中的所有类库。 3)System ClassLoader(加载%CLASSPATH%路径的类库)以及其它自定义的ClassLoader。 双亲委派模型:不管是应用程序类加载器还是自定义类加载器在进行类加载时它首先不会自己去加载, 它会把加载任务委派给自己的父类加载器,比如有个类需要自定义类加载器来加载,它会把它交给应用程序类加载器, 应用程序类加载器又会把任务交给扩展类加载器,一直往上提交,直到启动类加载器。启动类加载器如果在自己的扫描范围内能找到类, 它就会去加载,如果它找不到,它就会交给它的下一级子加载器去加载,以此类推。

77.什么是双亲委派机制?介绍一些运作过程,双亲委派模型的好处?

         答:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类, 而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此, 因此所有的请求都应该最终传送到顶层的启动类加载器中, 只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围没有找到所需的类时), 子加载器才会自己主动去加载。使用双亲委派模型的好处在于Java类随着它的类加载器一起具备了 一种带有优先级的层次关系。例如类java.lang.Object,它存在在rt.jar中, 无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的Bootstrap ClassLoader进行加载, 因此Object类在程序的各种类加载器环境中都是同一个类。

78,什么情况下我们需要破坏双亲委派模型?

        答:1)发生在双亲委派模型出现之前–即JDK1.2发布之前。 由于双亲委派模型是在JDK1.2之后才被引入的, 而类加载器和抽象类java.lang.ClassLoader则是JDK1.0时候就已经存在。 2)是这个模型自身的缺陷所导致的,双亲委派模型很好地解决了各个类加载器的基础类统 一问题(越基础的类由越上层的加载器进行加载),基础类之所以被称为“基础”,是因为它们总是作为被调用代码调用的API。 3)是由于用户对程序的动态性的追求导致的,例如OSGi的出现。在OSGi环境下, 类加载器不再是双亲委派模型中的树状结构,而是进一步发展为网状结构。

118.JVM如何加载字节码文件?

        答:1)加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区中的运行时数据结构, 在堆中生成一个代表这个类的java.lang.Class对象,作为方法区类数据的访问入口,这个过程需要类加载器参与; 2)链接:分为验证:确保加载的类信息符合JVM规范,没有安全方面的问题;准备:正式为类变量(static变量)分配内存 并设置类变量初始值的阶段,这些内存都将在方法去中进行分配;解析:虚拟机常量池的符号引用替换为字节引用过程; 3)初始化:初始化阶段是执行类构造器()方法的过程。类构造器()方法是由编译器自动收藏类中 的所有类变量的赋值动作和静态语句块(static块)中的语句合并产生;当初始化一个类的时候,如果发现其父类还没有进行过 初始化,则需要先触发其父类的初始化;虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步; 当范围一个Java类的静态域时,只有真正声名这个域的类才会被初始化;4)使用;5)卸载。

173.java中垃圾回收机制GC原理等?

        答:1.首先要知道为什么要垃圾回收,因为我们在不断的分配内存空间, 如果不进行垃圾回收,内存迟早会被消耗空,导致系统崩溃。 2.其次要知道哪些内存需要被回收,就是那些不可能再被任何途径使用的对象, 怎样找到这些对象就要用算法。(1).可达性分析法:就是通过一系列称为“GC Roots”的对 象作为起始点,从这些节点向下搜索,搜索所走过的路径称为引用链, 当一个对象到GC Roots没有任何引用链(即GC Roots到对象不可达)时,则证明此对象是不可用的。 (2).对象的引用的4种状态:当内存空间还足够时,则能保留在内存中;如果内存空间在进行垃圾收集后还是非常紧张, 则可以抛弃这些对象。 (1)强引用:只要强引用还存在,垃圾收集器永远不会回收被引用的对象。 (2)软引用:有些还有用但并非必需的对象。在系统将要发生内存溢出之前, 将会把这些对象列进回收范围进行二次回收。 (3)弱引用:描述非必需对象。被弱引用关联的对象只能生存到下一次垃圾回收之前, 垃圾收集器工作之后,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。 (4)虚引用:这个引用存在的唯一目的就是在这个对象被收集器回收时收到一个系统通知, 被虚引用关联的对象,和其生存时间完全没关系。 (3).方法区的垃圾回收: 1. 废弃常量。2. 无用的类 垃圾收集算法:(1).标记-清除(Mark-Sweep)算法:分为“标记”和“清除”两个阶段: 首先标记出所有需要回收的对象,标记完成后统一回收所有被标记的对象;从效率上讲该算法效率不高; 从空间上讲标记清除后会产生大量不连续的内存碎片。 (2).复制(Copying)算法:它将可用的内存分为两块,每次只用其中一块,当这一块内存用完了, 就将还存活着的对象复制到另外一块上面,然后再把已经使用过的内存空间一次性清理掉;缺点是内存缩小为了 原来的一半,这样代价太高了。 (3).标记-整理(Mark-Compact)算法:是让所有存活对象都向一端移动, 然后直接清理掉边界以外的内存。 (4).分代收集算法:大批对象死去、少量对象存活的(新生代), 使用复制算法,复制成本低;对象存活率高、没有额外空间进行分配担保的(老年代), 采用标记-清理算法或者标记-整理算法。 垃圾回收器:Serial收集器、ParNew收集器、Serial Old收集器、CMS收集器、G1收集器(使用较多)

124.GC分哪两种,Minor GC 和Full GC有什么区别?什么时候会触发Full GC?分别采用什么算法?

        答:1)Minor GC : 发生在新生代,   2)Full GC :发生在老年代   3)当老年代满时触发Full GC, 新生代采用复制算法, 老年代采用标记-清除、标记-整理算法 165.GC算法及几种垃圾收集器? 答:1)Serial收集器:采用复制算法的新生代收集器,它是一个单线程收集器, 只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集时, 必须暂停其他所有的工作线程,直至Serial收集器收集结束为止。 2)ParNew 收集器:采用复制算法的新生代收集器,使用多线程收集, 但它却是许多运行在Server模式下的虚拟机中首选的新生代收集器,其中有一个与性能无关的重要原因是, 除了Serial收集器外,目前只有它能和CMS收集器(Concurrent Mark Sweep)配合工作。 3)CMS收集器:使用标记清除算法,是一种以获取最短回收停顿时间为目标的收集器, 它非常符合那些集中在互联网站或者B/S系统的服务端上的Java应用。 4)G1收集器:使用标记整理算法,将整个Java堆划分为多个大小相等的独立区域(Region), 虽然还保留新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,而都是一部分Region(不需要连续)的集合。

145.JVM的编译优化?

        答:当虚拟机发现某个方法或代码块的运行特别频繁的时候,就会把这些代码认定为“热点代码”。 hotspot内的即时编译器分为两种 Client Compiler和 Server Compiler简称为c1和c2。 c1编译模式有三个阶段:1)前端将字节码构造成一种高级中间代码表示。在此之前,编译器会 在字节码上完成一部分基础优化,如 方法内联,常量传播等优化。2)后端从 HIR 中产生低级中间代码表示,而在此之前会在HIR 上完成另外一些优化,如空值检查消除,范围检查消除等。 3)在平台相关的后端使用线性扫描算法在LIR 上分配寄存器,做窥孔(Peephole)优化,然后产生机器码。 c2模式:采用了大量传统编译优化的技巧来进行优化,占用内存相对多一些,适合服务器端的应用。 逃逸分析是C2进行很多优化的基础,它根据运行状态来判断方法中的变量是否会被外部读取, 如不会则认为此变量是不会逃逸的,那么在编译时会做标量替换、栈上分配和同步消除等优化。 编译期优化:用来实现Java的各种新的语法特性,比如泛型,变长参数,自动装箱/拆箱; Java泛型只在编译期存在,编译完成后的字节码中会替换为原生类型。故称Java泛型为伪泛型;if语句中使用常量。 运行时优化:1)公共字表达式消除:如果一个表达式E已经被计算过了,没有必要花时间再对它 进行计算,只需要直接用前面计算过的表达式结果代替E就可以了。 2)数组边界检查消除:数组边界检查时必须做的,但数组边界检查在某些情况下可以简化。例如数组下标示一个常量。 3)方法内联:方法内联是编译器最重要的优化手段之一,除了消除方法调用的成本之外,更重要的是可以为其他优化手段建立良好的基础。 4)逃逸分析:逃逸分析(Escape Analysis)并不是直接优化代码的手段,而是为其他优化手段提供依据的分析技术。

146.对Java内存模型的理解,以及其在并发中的应用?

        答:Java内存模型是定义程序中各个变量的访问规则,在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节。 每条线程还有自己的工作内存,线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取、赋值等) 都必须在工作内存中进行,而不能直接读写主内存中的变量。不同的线程之间也无法直接访问对方工作内存中的变量,线程间 变量值的传递均需要通过主内存来完成。内存间使用lock(锁定)、unlock(解锁)、read(读取)、load(载入)、use(使用)、 assign(赋值)、store(存储)、write(写入)操作。 在并发中使用: 1)synchronized:同步不仅仅包括互斥,同样重要的还有可见性,同步块能够保证被之前线程写过的内存对后面进入同步块的线程可见。 2)final:修饰的变量都是不可变的,这也意味着一旦final字段被第一次初始化,后面都不会再出现对它的写操作,因此被final修饰的字段是线程安全的。 3)volatile:保证此变量对所有线程的可见性,修改的值对于其他线程来说是可以立即得知的。

147.指令重排序,内存栅栏等?

        答:指令重排序: 1)编译器优化重排序:编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。 2)指令级并行的重排序:如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。 3)内存系统的重排序:处理器使用缓存和读写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。 4)通过内存屏障禁止重排序:JMM通过插入特定类型的内存屏障,来禁止特定类型的编译器重排序和处理器重排序。 内存栅栏:是让一个CPU处理单元中的内存状态对其它处理单元可见的一项技术。该技术有volatile(volatile是用Lock前缀方式的内存屏障伪类型来实现的)。 内存栅栏的能力: 1)就像一套栅栏分割前后的代码,阻止栅栏前后的没有数据依赖性的代码进行指令重排序,保证程序在一定程度上的有序性。 2)强制把写缓冲区/高速缓存中的脏数据等写回主内存,让缓存中相应的数据失效,保证数据的可见性。 内存栅栏类型: 1)lfence:即读屏障(Load Barrier),在读指令前插入读屏障,可以让高速缓存中的数据失效,重新从主内存加载数据,以保证读取的是最新的数据。 2)sfence:即写屏障(Store Barrier),在写指令之后插入写屏障,能让写入缓存的最新数据写回到主内存,以保证写入的数据立刻对其他线程可见。 3)mfence,即全能屏障,具备ifence和sfence的能力。 4)Lock前缀:Lock不是一种内存屏障,但是它能完成类似全能型内存屏障的功能。

138.JVM垃圾回收机制,何时触发MinorGC等操作?

        答:1)eden区满时,触发MinorGC。即申请一个对象时,发现eden区不够用,则触发一次MinorGC。 2)Full GC之前调用,仅适用于Parallel Scavenge(-XX:+UseParallelGC),虚拟机运行在Server模式下的默认收集器组合。

139.新生代和老生代的内存回收策略?

        答:java虚拟机将java堆分为新生代和老生代,元空间 新生代:采用复制算法:将内存分为大小相等的两块,每次使用其中一块,当一块内存用完了, 就将还存活的对象分到另一块。然后把使用过的内存空间一次进行清理。这种算法的缺点就是会浪费一半的空间。 所以不需要按照1:1进行分配,而是按照内存分为一块较大的Eden空间和两块较小的Survivor空间(from和to两个), 每次使用Eden和其中一个Survivor。当回收时,将Eden和其中一块Survivor中还存活着的对象一次性复制到 另一块Survivor中,然后清理Eden和刚才用过的Survivor空间。Eden可通过-XX:SurvivorRatio=i来设置,默认i=8。 老年代:采用标记整理算法:先标记需要回收的对象,然后让所有存活的对象向一端移动,然后直接清理掉边界以外的内存。 1)对象首先在新生代Eden区中分配,当Eden没有足够空间分配时,虚拟机将发起一次Minor GC,GC后将已 有对象放入Survivor中,若Survivor空间不足,则通过分配担保机制提前转移到老年代。 2)大对象直接进入老年代,例如较长的字符串和较长的数组。虚拟机提供了一个-XX:PretenureSizeThreshold参数, 令大于这个值的参数直接在老年代分配。3)长期存活的对象进入老年代,通过对象年龄计数器,记录对象是否存活, 当达到年龄阈值(-XX:MaxTenuringThreshold设置)时进入老年代。4)动态对象年龄判定,如果在Survivor空间 中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代。 5)空间分配担保,检测对象在老年代的阈值,如果大于,,尽管这次Minor GC冒险,也会尝试进行一次Minor GC;如果小于, 或者HandlePromotionFailure设置为不允许冒险,那么需要进行一次Full GC。

98.如何把java内存的数据全部dump出来?

        答:1)获取内存详情:jmap -dump:format=b,file=e.bin pid 用 jvisualvm.exe 进行内存分析。 2)获取内存dump: jmap -histo:live pid 这种方式会先出发fullgc,所有如果不希望触发fullgc 可以使用jmap -histo pid。 3)jdk启动加参数: -XX:+HeapDumpBeforeFullGC -XX:HeapDumpPath=/httx/logs/dump 这种方式会产生dump日志,再通过jvisualvm.exe 或者Eclipse Memory Analysis Tools 工具进行分析。

140.jvm的一些命令?

        答: jps:显示指定系统内所有的HotSpot虚拟机进程。 jstat:显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。 jmap:生成heap dump文件。 jhat:用来分析jmap生成的dump。 jstack:用于生成虚拟机当前时刻的线程快照。 jinfo:是实时查看和调整虚拟机运行参数。

68.java内存模型,垃圾回收机制,不可达算法?

        答:内存模型也就是运行时的内存区域: 1)程序计数器:是一个较小的内存空间,是当前线程所执行的字节码的行号指示器;该内存是唯一一个没有内存溢出的区域。 2)java虚拟机栈:是线程私有的,每个方法执行的同时都要创建一个栈帧,用于储存局部变量表, 操作数栈,动态链接,方法出口,等信息;局部变量表放置了,编译器可知的各种基本数据类型,和对象引用。 3)本地方法栈:存储执行本地方法(native)时所需要的数据、指令、返回地址。 4)方法区:是线程共享区域,储存已经被虚拟机加载的,类信息,常量,静态变量,即时编译器(JIT)编译后的代码。 5)堆:是JVM所管理的内存中最大的一块,是被所有Java线程锁共享的,不是线程安全的, 堆可以分为新生代和老年代;新生代再细致一点有Eden空间、From Survivor空间、To Survivor空间等;是垃圾回收管理的主要区域。 垃圾回收机制: 1)引用计数算法:原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数。 2)根搜索算法:通过一些“GC Roots”对象作为起点,从这些节点开始往下搜索, 搜索通过的路径成为引用链,当一个对象没有被GC Roots的引用链连接的时候,说明这个对象是不可用的。 3)标记-清除算法:首先标记出需要回收的对象,标记完成之后统一清除对象。 4)复制算法:内存平均分成A、B两块。 5)标记-整理算法:首先标记出需要回收的对象,移动所有存活的对象,且按照内存地址次序依次排列, 然后将末端内存地址以后的内存全部回收。 6)分代收集算法:根据对象的存活周期的不同将内存划分为几块儿。一般是把Java堆分为新生代和老年代: 短命对象归为新生代,长命对象归为老年代。 不可达算法:是通过可达性分析来判定对象是否存活的。通过一些被称为引用链(GC Roots)的对象作为起点, 从这些节点开始向下搜索,当一个对象到GC Roots没有任何引用链相连时(即从GC Roots节点到该节点不可达),则证明该对象是不可用的。

69.类加载,类隔离机制?

        答:类加载:就是将类的.class文件中的二进制数据读到内存中,将其放在运行时数据区的方法区内,然后在堆区 创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类加载是位于堆中的Class对象, Class对象封装了类在方法区内的数据结构,并提供了访问方法区内的数据结构的接口。 加载.class文件的方式: 1)从本地系统中直接加载。 2)通过网络下载.class文件。 3)从zip,jar等归档文件中加载.class文件。 4)从专有数据库中提取.class文件。 5)将Java源文件动态编译为.class文件。 类的生命周期:加载、验证、准备、解析、初始化。 隔离机制:在Java中,所有的类默认通过ClassLoader加载,而 Java 默认提供了三层的 ClassLoader,并通过双亲委托模型的原则进行加载。 1)BootstrapClassLoader:加载{JVAV_HOME}jrelibrt.jar或者Xbootclasspath选项指定的jar包。 2)ExtensionClassLoader:加载{JVAV_HOME}jrelibext*.jar或者Djava.ext.dirs指定目录下的jar包。 3)AppClassLoader:加载CLASSPATH或者Djava.class.path指定目录下的class和jar包。 Java 中默认的 ClassLoader 都规定了其指定的加载目录,一般也不会通过 JVM 参数来使其加载自定义的目录, 所以我们需要自定义一个 ClassLoader 来加载装有不同版本的 jar 包的扩展目录,同时为了使运行扩展的 jar 包时, 与启动项目实现绝对的隔离,我们需要保证他们所加载的类不会有相同的 ClassLoader,根据双亲委托模型的原理可知, 我们必须使自定义的 ClassLoader 的 parent 为 null,这样不管是 JRE 自带的 jar 包或 一些基础的 Class 都不会委托给 App ClassLoader。

70.jvm相关,主要是内存分配的过程,何时出发GC,用什么工具或者命令来进行监控?

        答:JVM下所有线程共享的部分有: 方法区及堆;而每个独立运行的线程,各自又享有各自的1)虚拟机栈 2)程序计数器 3)本地方法栈。 分配过程主要讲解: 1)方法区:也就是永久代,永久代是线程共享的JVM内存区域,用于存储虚拟机已加载的类信息,常量,静态变量, 即使编译器编译后的代码等等数据,还会包括一些跟类有关的对象数组和类型数组,JVM使用的内部对象, 以及编译器优化的使用信息等。在过去的JVM版本中,常量池也是放在永久代的。但在HotSpot 的JDK 1.7开始, 字符串常量池已经从永久代中移出了。方法区的大小,在JVM启动参数里可以用-XX:MaxPermSize=XXXM来设置。 2)堆: 这是JVM所管理的最大的一块内存区域,被所有线程共享。该区域用来存放对象实例。 所有的Java对象实例及数组都要在堆上分配内存。JVM启动参数-Xmx及-Xms可用来控制堆内存大小。 GC过程主要讲解: 发生GC:一般的对象在Eden中,较大的对象会直接进入老年代这个由参数 -XX:PretenureSizeThreshold 设置。 在新生代中三个区域eden,from,to,一个时刻只会有两片内存被使用,首先eden肯定会被使用,from和to只有一片会被使用, 主要是由于虚拟机采用的复制算法。 1)minor gc(清理老年代):为了避免在gc的时候产生内存碎片,首先eden空间不足时会产生一次minor gc, 垃圾回收器会在eden和一片使用的Survivor中进行清理,存活下来的对象会被复制到to中(假设to的大小足够装满), 然后清空eden和from,保留下来的对象年龄加一。当年龄到达某一个设定值时会进入老年代,由参数 -XX:MaxTenuringThreshold设置。 2)full gc(清理整个空间包括年轻代和老年代):minor gc时Survivor区域不足以容纳年轻代中存活下来的对象时, 且老年代中剩余空间容纳不了新生代中存活下来的对象时会进行full gc。老年代中因为没有进行分区, 所以回收算法使用的是标记-清理算法或者标记整理算法。 监控工具讲解:jstack:用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息。 jvisualvm:通过界面时时查看程序运行状态。

3.java中垃圾回收机制GC原理等?

        答: 1.首先要知道为什么要垃圾回收,因为我们在不断的分配内存空间,如不垃圾回收,内存迟早会被消耗空,导致系统崩溃。 2.其次要知道哪些内存需要被回收:JVM会分配一个运行时内存空间。包括5部分:程序计数器(Program Counter Register)、 虚拟机栈(VM Stack)、本地方法栈(Native Method Stack)、方法区(Method Area)、堆(Heap)。其中程序计数器、虚拟机栈、 本地方法栈是每个线程私有内存空间,随线程而生,随线程而亡。这3个区域内存分配和回收都是确定的,无需考虑内存回收的问题。 但方法区和堆就不同了,一个接口的多个实现类需要的内存可能不一样,只有程序运行时才会创建对象,GC主要关注这部分内存。 GC主要对方法区和堆进行回收,涉及到多线程(指堆)、多个对该对象不同类型的引用(指方法区),才会涉及GC的回收。 (1).引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。 引用计数法实现简单,判定高效,但不能解决对象之间相互引用的问题。 (2).可达性分析法:就是通过一系列称为“GC Roots”的对象作为起始点,从这些节点向下搜索,搜索所走过的路径称为引用链, 当一个对象到GC Roots没有任何引用链(即GC Roots到对象不可达)时,则证明此对象是不可用的。 GC Roots对象有: (a)本地变量表中引用的对象。 (b)方法区中静态变量引用的对象。 (c)方法区中常量引用的对象。 (d)Native方法引用的对象。 当一个对象到 GC Roots 没有任何引用链时,意味着该对象可以被回收。 (3).方法区的垃圾回收: 1.废弃常量。2.无用的类 垃圾收集算法: (1).标记-清除(Mark-Sweep)算法:分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,标记完成后统一回收所有被标记的对象; 从效率上讲该算法效率不高;从空间上讲标记清除后会产生大量不连续的内存碎片。 (2).复制(Copying)算法:它将可用的内存分为两块,每次只用其中一块,当这一块内存用完了,就将还存活着的对象复制到另外一块上面, 然后再把已经使用过的内存空间一次性清理掉;缺点是内存缩小为了原来的一半,这样代价太高了。 (3).标记-整理(Mark-Compact)算法:是让所有存活对象都向一端移动,然后直接清理掉边界以外的内存。 (4).分代收集算法:大批对象死去、少量对象存活的(新生代),使用复制算法,复制成本低;对象存活率高、没有额外空间进行分配担保的(老年代),采用标记-清理算法或者标记-整理算法。 垃圾回收器主要有:Serial收集器、ParNew收集器、Serial Old收集器、CMS收集器、G1收集器(使用较多)

189.JVM垃圾回收机制,何时触发MinorGC(新生代GC)等操作?

        答:分代垃圾回收机制:不同的对象生命周期不同。把不同生命周期的对象放在不同代上,不同代上采用最合适它的垃圾回收方式进行回收。 JVM中共划分为三个代:年轻代、年老代和持久代, 年轻代:存放所有新生成的对象; 年老代:在年轻代中经历了N次垃圾回收仍然存活的对象,将被放到年老代中,故都是一些生命周期较长的对象; 持久代:用于存放静态文件,如Java类、方法等。 新生代的垃圾收集器命名为“minor gc”,老生代的GC命名为”Full Gc 或者Major GC”.其中用System.gc()强制执行的是Full Gc. 判断对象是否需要回收的方法有两种: 1.引用计数 当某对象的引用数为0时,便可以进行垃圾收集。 2.对象引用遍历 果某对象不能从这些根对象的一个(至少一个)到达,则将它作为垃圾收集。在对象遍历阶段,gc必须记住哪些对象可以到达,以便删除不可到达的对象,这称为标记(marking)对象。

 触发GC(Garbage Collector)的条件: 
 1)GC在优先级最低的线程中运行,一般在应用程序空闲即没有应用线程在运行时被调用。 
 2)Java堆内存不足时,GC会被调用。

143.GC的两种判定方法:引用计数与引用链?

        答:引用计数器:对象再被创建时,对象头里会存储引用计数器,对象被引用,计数器+1;引用失效,计数器 -1;GC时会回收计数器为0的对象; 引用链:程序把所有的引用看树形结构的图,从根节点开始寻找对应的引用节点并标记,当所有的引用节点寻找完毕之后,剩余的节点认为是不可达的无用节点, 会被回收;

144.GC的三种收集方法:标记清除、标记整理、复制算法的原理与特点?

        答:标记清除:分为标记和清除两个阶段:首先标记出所有的需要回收的对象,在标记完成以后统一回收所有被标记的对象; 适合在老年代回收,效率低,容易产生空间碎片; 标记整理:分为标记和整理两个阶段:首先标记出所有需要回收的对象,让所有存活的对象都向一端移动, 然后直接清理掉端边界以外的内存;适合在老年代回收,整理时间较长; 复制算法:它先将可用的内存按容量划分为大小相同的两块,每次只是用其中的一块,用完将存活的复制到另一块上,适合在新生代,只移动指针, 按序分配内存;

145.GC收集器有哪些?CMS收集器与G1收集器的特点?

        答:并行收集器:串行收集器使用一个单独的线程进行收集,GC 时服务有停顿时间 串行收集器:次要回收中使用多线程来执行 CMS 收集器是基于“标记—清除”算法实现的,经过多次标记才会被清除 G1 从整体来看是基于“标记—整理”算法实现的收集器,从局部 上来看是基于“复制”算法实现的;

143.Java 8的内存分代改进?

        答:java8中完全移除了持久代(PermGen),并运用了新的内存空间元空间(Metaspace); 1)Metaspace内存分配模型:对于类元数据的大多数内存分配都不会发生在本地内存, 被用于描述类元数据的类对象被移除。2)Metaspace容量:默认的类元数据分配限制于可用的本地内存。 3)Metaspace垃圾收集:一旦类元数据的使用量达到了“MaxMetaspaceSize”指定的值,垃圾回收将被触发, 过于频繁的Metaspace垃圾收集是类和类加载器发生内存泄露的征兆,说明应用程序内存大小不合适,需要调整。 4)Java堆空间影响:java8将一些杂项数据被移到了Java堆空间。 5)Metaspace 监控:可以通过HotSpot 1.8的详细的GC日志输出观察。

159.Java对象的生命周期?

        答:1.创建阶段(Created) :为对象分配存储空间, 开始构造对象,对类static成初始化等 2.应用阶段(In Use):对象至少被一个强引用持有着。 3.不可见阶段(Invisible):简单说就是程序的执行已经超出了该对象的作用域了。 4.不可达阶段(Unreachable):对象处于不可达阶段是指该对象不再被任何强引用所持有。 5.收集阶段(Collected):也就是jvm阶段 6.终结阶段(Finalized):也就是垃圾回收 7.对象空间重分配阶段(De-allocated)

160.堆是怎么存储的,插入是在哪里?

        答:堆其实是一个完全二叉树,数组用二叉树存储是因为我们不需要存储左右子节点的指针,单纯地通过数组的下标, 就可以找到一个节点的左右子节点和父节点; 堆还分为大堆和小堆: 每个节点的值都大于等于子节点的值是“大顶堆”。 每个节点的值都小于等于子节点的值是“小顶堆”。 堆的插入是在数组的最后一位,然后与前面的数据逐个对比排序;

168.happens-before规则? 这些规则都是路逻辑上的,这些都需要内存屏障保证; happens-before(前面一个操作的结果对后续操作是可见的);

        答:1)程序顺序规则:一个线程中,按照程序顺序,前面的操作 Happens-Before 于后续的任意操作。 2)监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。 3)volatile变量规则:对一个volatile变量的写操作, Happens-Before 于后续对这个 volatile 变量的读操作。 4)传递性:。 5)start()规则:主线程启动子线程后,子线程能看到启动子线程前的操作。 6)join()规则:主线程等待子线程结束后,能看到子线程的操作。 7)程序中断规则:对线程interrupted()方法的调用先行于被中断线程的代码检测到中断时间的发生。 8)对象finalize规则:一个对象的初始化完成(构造函数执行结束)先行于发生它的finalize()方法的开始。

142.对象创建方法,对象的内存分配,对象的访问定位?

        答:对象创建方法:JVM接收到一条new指令时,首先检查指令的参数能否在常量池中定位到类的符号引用,并检查这个符号引用代表的类是否已被加载、 连接和初始化过;如果没有,那必须先执行相应的类的加载过程; 对象的内存分配:指针碰撞,用过的内存在一边,空闲内存在另一边,中间放着一个指针作为分界点的指示器;空闲列表,虚拟机维护一个列表, 记录哪些内存是可用的,分配的时候从列表中找到一块足够大的空间划分给对象,并更新列表; 对象的访问定位:使用句柄,java堆会划分一块内存作为句柄池,reference中存的是对象的句柄地址;使用直接指针:reference中存的是 对象的地址,对象中分一小块内存保存类型数据的地址;

191.OOM错误,stackoverflow错误,permgen space错误?

        答:1)OOM错误(内存溢出,堆空间满了):java.lang.OutOfMemoryError;提高堆内存空间-Xmx1024m 2)stackoverflow错误(栈空间溢出,通常是深度递归导致):java.lang.StackOverflowError; 就是细致的检查stack trace,找出行号的重复模式。这些重复的行号代表了被递归调用的代码。 仔细审查代码,理解为何递归不终止。可以通过-Xss参数增加栈的大小,这个参数可以在项目配置或命令行指定。 3)permgen space错误(永久区域内存不足):java.lang.OutOfMemoryError: PermGen space; GC不会在主程序运行期对PermGen space进行清理,这种错误常见在web服务器对JSP进行pre compile的时候。 解决办法:加大内存空间。

192.JVM常用参数?

        答:1)-Xms20M: 设置堆的最小值为20M,必须以M为单位。 2)-verbose:gc:输出虚拟机中GC的详细情况。 3)-Xss128k:设置虚拟机栈的大小为128k 4)-XX:PermSize=10M:JVM初始分配的永久代的容量,必须以M为单位。 5)-XX:MaxPermSize=10M:JVM允许分配的永久代的最大容量,必须以M为单位,大部分情况下这个参数默认为64M

140.Eden和Survivor的比例分配等?

        答:JVM把年轻代分为了三部分:1个Eden区和2个Survivor区(分别叫from和to)。 默认比例为8:1,在年轻代中使用复制算法,并且不会产生碎片, 对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。 紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中, 仍存活的对象会根据他们的年龄值来决定去向。经过这次GC后,Eden区和From区已经被清空。 这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”, 新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。 MinorGC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。

141.什么时候会触发FullGC?

        答:除直接调用System.gc外,触发Full GC执行的情况有如下四种。 1)旧生代空间不足:只有在新生代对象转入、创建为大对象、大数组时才会出现不足的现象; 2)持久代空间满:存放的class的信息,当系统中要加载的类、反射的类和调用的方法较多时; 3)CMS垃圾回收:老年代和新生代空间不够用时;

190.对Java内存模型的理解,以及其在并发中的应用?

        答:Java内存模型的主要目标: 定义程序中各个变量的访问规则。 Java线程之间的通信由Java内存模型(本文简称为JMM)控制。 所有变量的存储都在主内存,每条线程还都有自己的工作内存,线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝, 线程对变量的所有操作必须在工作内存完成,而不能直接读取主内存中的变量。不同的线程直接无法访问对方工作内存中的变量, 线程间变量的传递均需要通过主内存来完成。 线程间通信: 1)首先,线程A把本地内存A中更新过的共享变量刷新到主内存中去。 2)然后,线程B到主内存中去读取线程A之前已更新过的共享变量。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/999397.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号