Java基础面试题整理----JVM方面(自用)(一)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_45348687/article/details/102702261

1. 请说明JVM内存模型,JVM内存分为哪几个区?如何处理内存泄漏问题

解答:

JVM内存空间分为五部分,分别是:方法区、堆、Java虚拟机栈、本地方法栈、程序计数器

  • i. 方法区----主要用来存放类信息、类的静态变量、常量、运行时常量池等,方法区的大小是可以动态扩展的。

  • ii. ----主要用来存放数组、类的实例对象、字符串常量池等

  • iii. Java虚拟机栈----描述Java方法运行过程的内存模型,Java虚拟机栈会为每一个即将执行的方法创建一个叫做“栈帧”的区域,该区域用来存储该方法运行时需要的一些信息(局部变量表、操作数栈、动态链接、方法返回地址等)。比如我们方法执行过程中需要创建变量时,就会将局部变量插入到局部变量表中,局部变量的运算、传递等在操作数栈中进行,当方法执行结束后,这个方法对应的栈帧将出栈,并释放内存空间。栈中会发生的两种异常:StackOverFlowError(栈溢出异常)、OutOfMemoryError(内存溢出异常)。(StackOverFlowError表示当前线程申请的栈超过了事先定好的栈的最大深度,但内存空间可能还有很多。而OutOfMemoryError是指当线程申请栈中发现栈已经满了,而且内存也全部用光了)

  • iv. 本地方法栈----本地方法栈结构上和Java虚拟机栈一样,只不过Java虚拟机栈是运行Java方法的区域,而本地方法栈是运行本地方法的内存模型。运行本地方法时也会创建栈帧,同样栈帧里也有局部变量表、操作数栈、动态链接和方法返回地址等,在本地方法执行结束后栈帧也会出栈并释放内存资源,也会发生OutOfMemoryError。

  • v. 程序计数器----程序计数器是一个比较小的内存空间,用来记录当前线程正在执行的那一条字节码指令的地址。如果当前线程正在执行的是本地方法,那么此时程序计数器为空。程序计数器有两个作用:1、字节码解释器通过改变程序计数器来一次读取指令,从而实现代码的流程控制,比如我们常见的顺序、循环、选择、异常处理等。2、在对线程的情况下,程序计数器用来记录当前线程执行的位置,当线程切换回来的时候仍然可以知道该线程上次执行到了那里。而且程序计数器是唯一一个不会出现OutOfMeroryError的内存区域。
    补充:方法区和堆都是线程共享的,在JVM启动时创建,在JVM停止时销毁,而Java虚拟机栈、本地方法栈、程序计数器时线程私有的,随线程的创建而创建,随线程的结束而死亡。

  • 内存泄漏怎么造成的: 对象已经没有被应用程序使用,但是垃圾回收器没办法移除他们,因为还在被引用着。由两个特点:1、这些对象时可达的,即在有向图中,存在通路可以与其相连;2、这些对象是无用的,即程序以后不会再使用这些对象。满足这两个条件,那么可以判定其为Java中的内存泄漏,这些对象不会被GC回收,然后它却占用内存。

  • 例如:

public class Text{
	Object object;
	public void method1(){
		object=new Object();
		//其他代码......
	}
}

这里的object实例,其实我们期望它只作用于method1()方法中,且其他地方不会再用到它,但是当menhod1()方法执行完成后,object对象所分配的内存不会马上被认为时可以被释放的对象,只有再Text类创建的对象被释放后才会释放,这就是一种内存泄漏。解决办法就是将object作为method1()方法中的局部变量。当然如果一定要这么写,可以写成如下:

public class Text{
	Object object;
	public void method1(){
		object=new Object();
		//其他代码......
		object=null;
	}
}

这样,之前“new Object()”分配的内存,就可以被GC回收。

  • 解决办法:

  • 1、解决的原则就是尽量减小对象的作用域(例如将类的成员变量改写为方法内的局部变量)以及手动设置null值。

  • 2、各种提供了close()方法的对象,比如数据库连接JDBC、网络连接scoket和io连接,习惯性的调用其close()方法(或者类似方法)将其关闭。

  • java内存泄漏原因、常见泄漏情况、分析和解决方法


2. 简述JVM原理

解答:

  • JVM体系结构:
    1、类装载器ClassLoader
    (用来装载 .class文件)
    2、执行引擎(执行字节码,或者执行本地方法)
    3、运行时数据区(方法去、堆、Java栈、程序计数器、本地方法栈)

  • JVM原理: JVM是Java的核心和基础,再java编译器和os平台之间的虚拟处理器。它是一种利用软件方法实现的抽象的计算机于下层的操作系统和硬件平台,可以在上面执行java的字节码程序。java编译器只要面向JVM,生成JVM能理解的代码或字节码文件。Java源文件经编译成字节码程序,通过JVM将每一条指令翻译成不同平台机器码,通过特定平台运行。

  • 深入详细讲解JVM原理


3. 描述一下JVM加载class文件的原理机制?

解答:

  • Java中的所有类,都需要由类加载器装载到JVM中才能运行。类加载器本身也是一个类,而它的工作就是把class文件从硬盘读取到内存中。在写程序的时候,我们几乎不需要关心类的加载,因为这些都是隐式装载的。
  • Java类的加载是动态的,它并不会一次性将所有类全部加载后在运行,而是保证程序运行的基础类(像是基类)完全加载到JVM中,至于其他类,则在需要的时候才加载。这当然是为了节省内存开销。
  • Java的类加载器有三个,对应Java的三种类:
    BootstrapLoader:负责加载系统类(指的是内酯类,像是String,它是由C++所撰写的)
    ExtClassLoader:负责加载扩展类(指的是继承类和实现类)
    AppClassLoader:负责加载应用类(程序员自定义的类)
    三个加载器各自完成自己的工作,为了解决协调工作的问题,Java采用了委托模型机制:当类加载器需要加载类的时候,先请示其Parent(及上一层加载器)在其搜索路径载入,如果找不到,才在自己的搜索路径搜索该类。这样的顺序其实就是加载器层次上自顶而下的搜索,因为加载器必须保证基础类的加载。之所以是这种机制,还有一个安全上的考虑:如果某人将一个恶意的基础类加载到jvm,委托模型机制会搜索其父类加载器,显然是不可能找到的,自然就不会将该类加载进来。
  • 原理机制非常简单,就是下面几个步骤:
    1.装载:查找和导入class文件;
    2.连接: (1)检查:检查载入的class文件数据的正确性;
    (2)准备:为类的静态变量分配存储空间;
    (3)解析:将符号引用转换成直接引用(这一步是可选的)
    3.初始化: 初始化静态变量,静态代码块。

4. Java中垃圾回收有什么目的?什么时候进行垃圾回收?

解答:

  • 垃圾回收是在内存中存在没有引用的对象或超过作用域的对象时进行垃圾回收,垃圾回收的目的时识别并且丢弃不再使用的对象来释放和重用资源。
  • 目的: 回收堆中不再使用的对象,释放资源;
  • 回收时间:当对象失去引用后,系统会在何时的时间回收它所占的内存; 回收时间就是发生GC的时间。
    在新生代的Eden区满了会触发新生代GC(MiMor
    GC),经过多次(默认15次)触发新生代GC存活下来的对象就会升级到老生代,升级到老生代的对象所需要的内存大于老年代剩余内存,则会触发老生代GC(Full
    GC)。 当程序调用Syetem.gc()时也会出发Full GC。
    Java中垃圾回收的目的?什么时候进行?

5. 什么是字节码?采用字节码的最大好处?什么是java虚拟机?

解答:


6. Java堆的结构是什么样子的?什么是堆中的永久代(Perm Gen space)?

解答:

  • java堆不是数据结构意义上的堆,而是JVM的堆,也即是运行时的数据区。所有类的实例和数组都是在堆上分配内存,它是JVM启动时被创建,对象所占的内存是由自动内存管理系统也就是垃圾回收器回收。
  • 堆内存是由存活的对象以及死亡的对象组成的。存活的对象不会被垃圾回收期回收;死亡的对象是还没有被垃圾回收器回收的对象,等下一个周期回收。
  • 永生代:永生代主要存在类定义,字节码,和常量等很少会变更的信息。并且永生代不会发生垃圾回收,如果永久代满了或者超过了临界值,会触发完全垃圾回收(Full
    GC) 在java8中,已经移除了永生代,就加了一个叫做元数据区的native内存区。
    java堆结构,以及堆中的永久代

猜你喜欢

转载自blog.csdn.net/weixin_45348687/article/details/102702261