2019-08-03 java底层知识JVM面试核心

一.谈谈你对java的理解

1.java与平台无关:一次编译,处处运行
2.GC,不需要我们手动的回收垃圾
3.语言特性:泛形,反射,Lamda表达式
4.面向对象:封装,继承,多态
5.类库:JUC,IO,集合
6.异常处理:

二.java是如何实现与平台无关的

编译:javac,将源码编译成字节码class文件
class文件保存的是java文件翻译成的二进制字节码,还会添加一个共有的静态常量属性.class,这个属性记录类的相关信息及类型信息,是class的一个实例,

Javap:是JDK自带的反编译命令,在命令行当中可以执行。

一次编译,处处运行

运行:JVM虚拟机会把.class文件解析成本平台可以识别的二进制机器码,。class文件仅仅是面向JVM的。

三.JVM如何加载.class文件

JVM是一个内存当中的虚拟机

JVM

JVM主要由Class Loader,Runtime Data Area,Rxcution Engine,Native Interface几部分组成,它主要通过Class Loader将符合其格式的.class文件加载到内存里面,并通过Execution Engine对class里面的字节码进行解析,并提交给操作系统去执行

Native Interface(本地接口):主要作用是融合不同开发语言的库(C/C++),供给java调用。

1.Class Loader

Class Loader在java当中有非常重要的作用,它主要工作在class加载的记载阶段,其作用是将从系统外部获得字节码文件加载到系统当中,然后由jvm进行连接/初始化等工作,他是java的核心组件,所有的class都是通过它加载的。

Class Loader类型:
BootStrapClassLoader(启动类加载器):C++编写,加载核心库,java.
ExtClassLoader(标准类加载器):java编写,加载扩展库,javax.
AppClassLoader:java编写,加载程序所在目录
自定义ClassLader:java编写,定制化加载

2.class类的加载过程

加载--->连接--->初始化--->使用--->卸载

class加载过程

类的加载方式:
1.隐式:new
2.显示:ClassLoader,Class.forName()

-- 对于显示加载来说,当我们获得到Class对象(clazz)以后,可以调用Class对象的newInstance()方法来生成对象的实例(root),
加载步奏:
1. 通过一个类的全限名来获取定义此类的二进制节流。(实现这个代码模块就是类加载器)
2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3. 在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据的 访问入口。

3.类加载的双亲委派机制

类加载的双亲委派机制
   假设有个类Person.class,当类加载器收到加载类的请求时,它首先会去检查自定义ClassLoader是否加载过这个类,如果没有,就把这个请求转交给父类加载器
去完成,依次类推,当Bootstrap ClassLoader也没有加载过这个类时,就会去自己的路径下加载,有就返回,没有就转交给自己的下一级Extension Class Loader去加载。

4.破坏双亲委派模型

1.在 JDK1.2 之前,用户去继承 java.lang.ClassLoader 的唯一目的就是为了重写 loadClass
方法,由于用户自己重写了 loadClass,那么也就是用户自己去自定义加载类,会破坏。


自定义Class Loader
自定义ClassLoader:
1.继承java.lang.ClassLoader 
2.重写fingClass(),defindClass()

四.JVM内存区域划分

参考自:[https://blog.csdn.net/javazejian/article/details/72772461]

image.png

Java虚拟机在运行程序时会把其自动管理的内存划分为以上几个区域,每个区域都有的用途以及创建销毁的时机,其中蓝色部分代表的是所有线程共享的数据区域,而绿色部分代表的是每个线程的私有数据区域。

1.程序计数器:

是一块较小的内存空间,在JVM当中,字节码解释器工作时就是通过改变计数器的值来选取下一跳需要执行的字节码指令,包括分支、循环、跳转、异常处理、线程恢复等基础功能,,都需要借助这个计数器来完成。由于JVM的多线程是通过线程切换并分派处理器来完成的,在一个确定的时刻,一个处理器只会处理一个线程当中的指令,因此为了线程切换后可以恢复到正确的位置,程序计数器设为私有的,记录线程执行步奏
程序计数器,只会为java方法计数,其他的native方法不会计数
由于程序记录器只是记录了行号,所以程序计数器不会有内存泄漏的问题

2.Java虚拟机栈

包括多个栈帧,即方法运行期间的基础数据结构,当方法调用结束时,帧才会被销毁,虚拟机栈又称为java堆栈。属于线程私有的数据区域,与线程同时创建,每个方法执行时都会创建一个栈桢来存储方法的的变量表、操作数栈、动态链接方法、返回值、返回地址等信息。
每次方法调用时,一个新的栈帧创建,并压栈到栈顶,当方法正常返回或者抛出未俘获的异常是,栈帧就会出栈,除了栈帧的压栈和出栈,栈不能直接被操作。

可以把栈帧看作是栈的一小块组成部分

虚拟机栈也是JVM管理的,它类似与一个集合,但是有固定长度,是由多个栈帧合起来的,编写程序的时候,每调用一个方法,JVM就会自动在内存当中分配一块内存空间(栈帧),方法调用结束以后,栈帧就会被自动释放掉,这也就是我们常说的,栈的内存不会通过GC去回收,而是会自己释放。

3.本地方法栈

和虚拟机栈相比:本地方法栈主要作用与标注了native的方法。
java方法用到的是虚拟机栈,而带有native关键字的方法用到的是本地方法栈。

4.方法区

方法区只是一个逻辑概念,《Java虚拟机规范》只是规定了有方法区这么个概念和它的作用,并没有规定如何去实现它。方法区的实现一般有两种,即MetaSpace(元空间)和PermGen(永久代)。
主要用于存储已被虚拟机加载的类信息(包括class对象的method,field等)、常量、静态变量、即时编译器编译后的代码等数据,根据Java 虚拟机规范的规定,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError 异常。

在JDK1.8以后,开始把类的元数据存放在本地堆内存当中,这一块区域就叫做MetaSpace,该区域在8之前是属于永久带的,原数据空间和永久代都是用来存放Class的相关信息的,包括class对象的method,field等,元空间和永久代皆是方法去的实现,只是实现的方法不一样,所以说方法区只是一种JVM的规范。

image.png

理论上本地内存有多大,MetaSpace就有多大,这解决了空间不足的问题,不过也不可能无限大,JVM会根据需要动态的确定其大小.

image.png

永久代会为GC带来不必要的复杂度,而且效率偏低,可能会随着每一次GC而发生移动,PermGen的每一种垃圾回收器都需要特殊处理永久代当中的信息.

5.堆

Java 堆也是属于线程共享的内存区域,它在虚拟机启动时创建,是Java 虚拟机所管理的内存中最大的一块,主要用于存放对象实例,几乎所有的对象实例都在这里分配内存,注意Java 堆是垃圾收集器管理的主要区域,因此很多时候也被称做GC 堆,如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError 异常。
JDK.7以后,原来位于方法去里面的常量池被移到了java堆当中,并且8以后使用元空间替代了永久代.

java堆

现在的垃圾回收器都是按照分代收集算法,java堆可以分为新生代和老年代,在细分为Eden,Survivor。

6.发生在虚拟机栈当中的StackOverflowError(栈溢出)和OutOfMemorryError(内存泄漏)

1.StackOverflowError(栈溢出)
image.png

StackOverflowError(栈溢出):递归的程序太多了

当线程执行一个方法时,就会创建一个对应的栈帧,并将建立的栈帧压入虚拟机栈当中,方法执行完毕的时候,就会将栈帧出栈,因此线程当前所执行的方法所对应的栈帧必定在栈的顶部,而我们的递归函数不断去调用自己,每一次方法调用就会生成一个栈帧,而且会保存当中当前方法的栈帧状态,将它放在JVM栈当中,栈帧上下文切换时候,可以切换到最新的方法栈帧当中。而由于我们每个线程的虚拟机栈的深度是固定的,递归实现将导致栈深度的增加,每次递归都会往栈里面压一个栈帧,如果超出了最大允许的深度,就会报错。

2.OutOfMemorryError

OutOfMemorryError(内存泄漏):

当虚拟机栈可以动态扩展时,无法申请足够多的内存就会报这个错误,

image.png

image.png

image.png

五.JVM三大性能调优参数—Xms —Xmx —Xss的含义

-Xss:规定了每个线程虚拟机栈的大小,一般256即可,此配置将会影响此进程当中并发线程数的大小

-Xms:初始的Java堆的大小,和该进程刚创建出来的时候,专属Java堆的大小,当对象容量超过了java堆的大小,就会扩容到 -Xmx大小

-Xmx:java堆能够达到的最大值
image.png

在很多的场合当中,我们通常将-Xms和-Xmx设置成一样的,因为当堆不够用而扩容时,发生内存抖动,影响程序运行时的稳定性。

六.java内存当中栈堆的区别和内存分配策略

程序运行时有三种内存分配策略,静态的,栈式的,堆式的

image.png

静态的:在编译时就可以确定运行时在内存当中的存储需求。

栈式的:可以称为动态的存储分配,是由一个类似于堆栈的运行栈来实现的,

堆式的:可变长度串,对象实例
JVM自己可以根据内存栈进行管理操作,

堆空间即即使是有垃圾回收机制,还是要考虑其释放的问题,栈的效率高于堆
image.png

七.内存泄露和内存溢出

image.png

你可能感兴趣的:(2019-08-03 java底层知识JVM面试核心)