java面试专题--jvm虚拟机

1.jvm 进程内存逻辑结构

主要分为三大块
1.堆
2.方法区
3.本地内存

这里写图片描述

2.jvm内存结构

这里写图片描述
程序计数器: 较小的内存空间, 当前线程执行的字节码的行号指示器(对java多线程起关键做用,多线程程序会不断切换线程,记录当前线程执行的行数,以便切回来的时候能继续执行);各线程之间独立存储,互不影响。
java栈: 线程私有,生命周期和线程,每一个方法在执行的同时都会建立一个栈帧用于存储局部变量表,操做数栈,动态连接,方法出口等信息。方法的执行就对应着栈帧在虚拟机栈中入栈和出栈的过程;栈里面存放着各类基本数据类型和对象的引用。栈帧解释以下图。
这里写图片描述
本地方法栈: 本地方法栈保存的 是native方法的信息,当一个 JVM建立的线程调用native方法 后,JVM再也不为其在虚拟机栈中 建立栈帧,JVM只是简单地动态 连接并直接调用native方法。
堆:堆数据区是用来存放对象和数组(特殊的对象)。堆内存由多个线程共享。堆内存随着JVM启动而建立。(重点关注:涉及到 内存的分配(new关键字,反射 等)与回收(回收算法,收集器 等))
方法区:也叫永久区,用于存储已经被虚拟机加载的类信息,常量,静态变 量(static变量)等数据。
运行时常量池::运行时常量池是方法区的一部分,用于存放编译期生成的各类字面量和符号引用。
本地内存:不是虚拟机运行时数据区的一部分,也不是 java虚拟机规范中定义的内 存域; 若是使用了NIO,这块区域 会被频繁使用,在java堆 内能够用directByteBuffer 对象直接引用并操做这块内存不受java堆大小限制,但受本机总内存的限制,能够经过MaxDirectMemorySize来设置(默认与堆内存最大值同样),因此也会出现 OOM异常。html

3.线程共享与线程私有

这里写图片描述

4.栈和堆的区别

1.功能
  栈以栈帧的方式存储方法调用的过程,并存储方法调用过程当中基本数据类型的变量(int、short、long、byte、float、double、boolean、char等)以及对象的引用变量,其内存分配在栈上,变量出了做用域就会自动释放; 

  而堆内存用来存储Java中的对象。不管是成员变量,局部变量,仍是类变量, 它们指向的对象都存储在堆内存中; 
2.线程独享仍是共享
  栈内存归属于单个线程,每一个线程都会有一个栈内存,其存储的变量只能在其所属线程中可见,即栈内存能够理解成线程的私有内存。 

  堆内存中的对象对全部线程可见。堆内存中的对象能够被全部线程访问。

3.空间大小
  栈的内存要远远小于堆内存,栈的深度是有限制的,若是递归没有及时跳出,极可能发生StackOverFlowError问题。 
  能够经过-Xss选项设置栈内存的大小。-Xms选项能够设置堆的开始时的大小,-Xmx选项能够设置堆的最大值

这里写图片描述

5.线程不安全的本质

这里写图片描述
例子:多个线程同时访问堆内存中的count变量,线程A操做时先把该变量读到当前线程内存中,此时线程B也这么作,当线程A作完一系列操做,再把变量写回堆内存中,线程B也如此。那么至关于同一份数据多个线程进行了相同重复的操做致使了线程的不安全。只有保证可见性和原子性才能保证线程的安全。java

6. JDK1.6, JDK1.7,JDK1.8的区别

JDK1.6和JDK1.7的区别:JDK1.7把**运行时的常量池**从方法区移到堆里面。
JDK1.7和JDK1.8的区别:JDK1.8把方法区去除,增长了元空间(metaspace)直接存放于本地内存。

这里写图片描述

这里写图片描述

这里写图片描述

7.为何去除方法区

永久代来存储类信息、常量、静态变量等数据不是个好主意, 很容易遇到内存
溢出的问题.JDK8的实现中将类的元数据放入native memory, 将字符串池和类
的静态变量放入java堆中. 可使用MaxMetaspaceSize对元数据区大小进行调
整。
   对永久代进行调优是很困难的,同时将元空间与堆的垃圾回收进行了隔离,避免
永久代引起的Full GC和OOM等问题。

8.jvm经常使用内存参数设置

这里写图片描述
注意:java8去掉了-XX:PermSize和-XX:MaxPermSize,新增了-XX:MetaspaceSize和MaxMetaspaceSizeweb

9.有哪些java内存溢出异常

java内存溢出异常主要有两个: 
  OutOfMemeoryError:当堆、栈(多线程状况)、方法区、元数据区、直接内存中数据达到最大容量时产生; 
  StackOverFlowError:若是线程请求的栈深度大于虚拟机锁容许的最大深度,将抛出StackOverFlowError,其本质仍是数据达到最大容量;

10.什么状况下出现堆溢出?怎么解决?

产生缘由
堆用于存储实例对象,只要不断建立对象,而且保证GC Roots到对象之间有
引用的可达,避免垃圾收集器回收实例对象,就会在对象数量达到堆最大容量时
产生OutOfMemoryError异常。 算法

解决办法
使用-XX:+HeapDumpOnOutOfMemoryError可让java虚拟机在出现内存溢
出时产生当前堆内存快照以便进行异常分析,主要分析那些对象占用了内存;也
可以使用jmap将内存快照导出;通常检查哪些对象占用空间比较大,由此判断代码
问题,没有问题的考虑调整堆参数。数组

11.什么状况下出现栈溢出?怎么解决?

产生缘由
若是线程请求的栈深度大于虚拟机锁容许的最大深度,将抛出StackOverFlowError;
若是虚拟机在扩展栈时没法申请到足够的内存空间,抛出OutOfMemeoryError; tomcat

解决办法
StackOverFlowError 通常是函数调用层级过多致使,好比死递归、死循环;
OutOfMemeoryError通常是在多线程环境才会产生,通常用“减小内存的方法”,既
减小最大堆和减小栈容量来换取更多的线程支持。安全

12.什么状况下出现方法区或元数据区溢出?怎么解决?

产生缘由
jdk 1.6之前,运行时常量池仍是方法区一部分,当常量池满了之后(主要是字符串变量),会抛出OOM异常;
方法区和元数据区还会用于存放class的相关信息,如:类名、访问修饰符、常
量池、方法、静态变量等;当工程中类比较多,而方法区或者元数据区过小,
在启动的时候,也容易抛出OOM异常;
解决办法
jdk 1.7以前,经过-XX:PermSize,-XX:MaxPerSize,调整方法区的大小;
jdk 1.8之后,经过-XX:MetaspaceSize ,-XX:MaxMetaspaceSize,调整元数据
区的大小;多线程

13.什么状况下出现本机直接内存溢出?怎么解决?

产生缘由
jdk自己不多操做直接内存,而直接内存(DirectMemory)致使溢出最大的特征是,
Heap Dump文件不会看到明显异常,而程序中直接或者间接的用到了NIO;
解决办法
直接内存不受java堆大小限制,但受本机总内存的限制,能够经过
MaxDirectMemorySize来设置(默认与堆内存最大值同样) jvm

14.垃圾回收知识

垃圾回收主要回收的是堆内存,基于分代的思想,以下图
这里写图片描述svg

15.内存分配和垃圾回收

参考:
内存分配和垃圾回收

GC过程图示参考

16.类的生命周期

这里写图片描述

17.何时出发类加载?

1. 使用new关键字实例化对象,读取或者设置一个类的静态变量的时候,调用类 的静态方法的时候; 
 2. 对类进行反射调用的时候; 
 3. 初始化子类时,父类会先被初始化; 
 4. 对类使用动态代理的时候须要先被初始化

18.双亲委派模型理解?

这里写图片描述
这里写图片描述

19.双亲委派模型好处

Java类随着它的类加载器一块儿具有了带有优先级的层次关系,保证java程序稳定运行。例子:好比本身的库也定义Obejct类,包名和jdk的也同样。这时候双亲委派的加载机制好处就很明显了。

20.tomcat类加载机制

这里写图片描述
Tomcat目录结构中,有三组目录(“/common/”,“/server/”和“shared/”)能够存放公用Java类库,此外还有第四组Web应用程序自身的目录“/WEB-INF/”, 把java类库放置在这些目录中的含义分别是:
放置在common目录中:类库可被Tomcat和全部的Web应用程序共同使用。
放置在server目录中:类库可被Tomcat使用,但对全部的Web应用程序都不可见。
放置在shared目录中:类库可被全部的Web应用程序共同使用,但对Tomcat本身 不可见。
放置在/WebApp/WEB-INF目录中:类库仅仅能够被此Web应用程序使用,对 Tomcat和其余Web应用程序都不可见。
注意:tomcat的类加载机制是违反了双亲委托原则的,对于一些未加载的非基础类(Object,String等),各个web应用本身的类加载器(WebAppClassLoader)会优先加载,加载不到时再交给commonClassLoader走双亲委托。

21. 同一个tomcat容器下的两个应用以及lib目录中都有UserServiceImpl类,tomcat怎 么样保证类的隔离性?

类加载器与类的惟一性:类加载器虽然只用于实现类的加载动做,可是对于任意一个类, 都须要由加载它的类加载器和这个类自己共同确立其在Java虚拟机中的惟一性。通俗的说,JVM中两个类是否“相等”,首先就必须是同一个类加载器加载的,不然,即便这两个类来源于同一个Class文件,被同一个虚拟机加载,只要类加载器不一样,那么这两个类一定是不相等的。