5、说说堆和栈的区别
栈是运行时单位,代表着逻辑,内含基本数据类型和堆中对象引用,所在区域连续,没有碎片;堆 是存储单位,代表着数据,可被多个栈共享(包括成员中基本数据类型、引用和引用对象),所在 区域不连续,会有碎片。
1、功能不同
栈内存用来存储局部变量和方法调用,而堆内存用来存储Java中的对象。无论是成员变量,局部变 量,还是类变量,它们指向的对象都存储在堆内存中。
2、共享性不同
栈内存是线程私有的。 堆内存是所有线程共有的。
3、异常错误不同
如果栈内存或者堆内存不足都会抛出异常。 栈空间不足:java.lang.StackOverFlowError。 堆空间 不足:java.lang.OutOfMemoryError。
4、空间大小
栈的空间大小远远小于堆的。
6、 什么时候会触发FullGC
除直接调用System.gc外,触发Full GC执行的情况有如下四种。 1. 旧生代空间不足 旧生代空间只有 在新生代对象转入及创建为大对象、大数组时才会出现不足的现象,当执行Full GC后空间仍然不
足,则抛出如下错误: java.lang.OutOfMemoryError: Java heap space 为避免以上两种状况引起 的FullGC,调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不 要创建过大的对象及数组。
- Permanet Generation空间满 PermanetGeneration中存放的为一些class的信息等,当系统中 要加载的类、反射的类和调用的方法较多时,Permanet Generation可能会被占满,在未配置为采 用CMS GC的情况下会执行Full GC。如果经过Full GC仍然回收不了,那么JVM会抛出如下错误信
息: java.lang.OutOfMemoryError: PermGen space 为避免Perm Gen占满造成Full GC现象,可 采用的方法为增大Perm Gen空间或转为使用CMS GC。
- CMS GC时出现promotion failed和concurrent mode failure 对于采用CMS进行旧生代GC的 程序而言,尤其要注意GC日志中是否有promotion failed和concurrent mode failure两种状况,当 这两种状况出现时可能会触发Full GC。 promotionfailed是在进行Minor GC时,survivor space放 不下、对象只能放入旧生代,而此时旧生代也放不下造成的; concurrent mode failure是在执行 CMS GC的过程中同时有对象要放入旧生代,而此时旧生代空间不足造成的。 应对措施为:增大
survivorspace、旧生代空间或调低触发并发GC的比率,但在JDK 5.0+、6.0+的版本中有可能会由 于JDK的bug29导致CMS在remark完毕后很久才触发sweeping动作。对于这种状况,可通过设置- XX:CMSMaxAbortablePrecleanTime=5(单位为ms )来避免。 - 统计得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间 这是一个较为复杂的触发 情况,Hotspot为了避免由于新生代对象晋升到旧生代导致旧生代空间不足的现象,在进行Minor GC时,做了一个判断,如果之前统计所得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩 余空间,那么就直接触发Full GC。 例如程序第一次触发MinorGC后,有6MB的对象晋升到旧生
代,那么当下一次Minor GC发生时,首先检查旧生代的剩余空间是否大于6MB,如果小于6MB ,
则执行Full GC。 当新生代采用PSGC时,方式稍有不同,PS GC是在Minor GC后也会检查,例如上 面的例子中第一次Minor GC后,PS GC会检查此时旧生代的剩余空间是否大于6MB,如小于,则触 发对旧生代的回收。 除了以上4种状况外,对于使用RMI来进行RPC或管理的Sun JDK应用而言,默 认情况下会一小时执行一次Full GC。可通过在启动时通过-java-
Dsun.rmi.dgc.client.gcInterval=3600000来设置Full GC执行的间隔时间或通过-XX:+ DisableExplicitGC来禁止RMI调用System.gc。
7、什么是Java虚拟机?为什么Java被称作是“平台无关的编程语 言” ?
Java虚拟机是一个可以执行Java字节码的虚拟机进程。Java源文件被编译成能被Java虚拟机执行的字 节码文件。 Java被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一个平台单独 重写或者是重新编译。Java虚拟机让这个变为可能,因为它知道底层硬件平台的指令长度和其他特 性。
8、Java内存结构
方法区和对是所有线程共享的内存区域;而java栈、本地方法栈和程序员计数器是运行是线程私有 的内存区域。
Java堆(Heap),是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内 存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实 例都在这里分配内存。
方法区(Method Area ),方法区(Method Area )与Java堆一样,是各个线程共享的内存区 域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数 据。
程序计数器(Program Counter Register),程序计数器(Program Counter Register)是一块 较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。
JVM栈(JVM Stacks ),与程序计数器一样,Java虚拟机栈(Java Virtual Machine Stacks)也是 线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方 法被执行的时候都会同时创建一个栈帧(Stack Frame )用于存储局部变量表、操作栈、动态
链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机 栈中从入栈到出栈的过程。
本地方法栈(Native Method Stacks ),本地方法栈(Native Method Stacks)与虚拟机栈所发 挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服
务,而本地方法栈则是为虚拟机使用到的Native方法服务。
9、说说对象分配规则
对象优先分配在Eden区,如果Eden区没有足够的空间时,虚拟机执行一次Minor GC。
大对象直接进入老年代(大对象是指需要大量连续内存空间的对象)。这样做的目的是避免在 Eden区和两个Survivor区之间发生大量的内存拷贝(新生代采用复制算法收集内存)。
长期存活的对象进入老年代。虚拟机为每个对象定义了一个年龄计数器,如果对象经过了1次 Minor GC那么对象会进入Survivor区,之后每经过一次Minor GC那么对象的年龄加1,知道达 到阀值对象进入老年区。
动态判断对象的年龄。如果Survivor区中相同年龄的所有对象大小的总和大于Survivor空间的 一半,年龄大于或等于该年龄的对象可以直接进入老年代。
空间分配担保。每次进行Minor GC时,JVM会计算Survivor区移至老年区的对象的平均大小, 如果这个值大于老年区的剩余值大小则进行一次Full GC,如果小于检查
HandlePromotionFailure设置,如果true则只进行Monitor GC,如果false则进行Full GC。
10、描述一下JVM加载class文件的原理机制?
JVM中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java中的类加载器是一个重要 的Java运行时系统组件,它负责在运行时查找和装入类文件中的类。 由于Java的跨平台性,经过编 译的Java源程序并不是一个可执行程序,而是一个或多个类文件。当Java程序需要使用某个类时,
JVM会确保这个类已经被加载、连接(验证、准备和解析)和初始化。类的加载是指把类的.class文 件中的数据读入到内存中,通常是创建一个字节数组读入.class文件,然后产生与所加载类对应的 Class对象。加载完成后,Class对象还不完整,所以此时的类还不可用。当类被加载后就进入连接 阶段,这一阶段包括验证、准备(为静态变量分配内存并设置默认的初始值)和解析(将符号引用 替换为直接引用)三个步骤。最后JVM对类进行初始化,包括:1)如果类存在直接的父类并且这个 类还没有被初始化,那么就先初始化父类;2)如果类中存在初始化语句,就依次执行这些初始化语
句。 类的加载是由类加载器完成的,类加载器包括:根加载器(BootStrap)、扩展加载器
( Extension)、系统加载器(System)和用户自定义类加载器(java.lang.ClassLoader的子
类)。从Java 2(JDK 1.2)开始,类加载过程采取了父亲委托机制(PDM )。 PDM更好的保证了 Java平台的安全性,在该机制中,JVM自带的Bootstrap是根加载器,其他的加载器都有且仅有一个 父类加载器。类的加载首先请求父类加载器加载,父类加载器无能为力时才由其子类加载器自行加 载。JVM不会向Java程序提供对Bootstrap的引用。下面是关于几个类加载器的说明:
Bootstrap:一般用本地代码实现,负责加载JVM基础核心类库(rt.jar);
Extension:从java.ext.dirs系统属性所指定的目录中加载类库,它的父加载器是Bootstrap;
System:又叫应用类加载器,其父类是Extension。它是应用最广泛的类加载器。它从环境变 量classpath或者系统属性java.class.path所指定的目录中记载类,是用户自定义加载器的默认 父加载器。
11、说说Java对象创建过程
1.JVM遇到一条新建对象的指令时首先去检查这个指令的参数是否能在常量池中定义到一个类的符 号引用。然后加载这个类(类加载过程在后边讲)
2.为对象分配内存。 一种办法“指针碰撞”、一种办法“空闲列表” ,最终常用的办法“本地线程缓冲分 配(TLAB)”
3.将除对象头外的对象内存空间初始化为0
4.对对象头进行必要设置