JVM虚拟机面试题-补漏(资料整理)

参考:JVM中的数据类型
参考:JVM之数据类型
参考:Java对象结构及大小计算
参考:java对象结构
参考:深入理解 JVM 中的 returnAddress
参考:jvm的生命周期和java类的生命周期
参考:JVM 工作原理和流程
参考:JVM调优总结
参考:Java对象的四种引用类型
参考:JAVA后端知识点碎片化整理 基础篇(十三) 了解GC

1.JVM与实现原理

在这里插入图片描述

2.JVM生命周期

1.JVM实例的诞生

当一个程序启动,伴随的就是一个jvm实例的诞生,当这个程序关闭退出,这个jvm实例就随之消亡。如果在同一台机器上运行多个程序,将诞生相应数量的jvm实例,每个程序都有一个与之对应的jvm实例供其运行。任何一个拥有公开的(public)、静态的(static)、没有返回值(void)并且接受一个字符串数组参数(String[] args) 的main()函数的class都可以作为JVM实例运行的起点 。

2.JVM实例的运行

main()作为该程序初始线程的起点,任何其他线程均由该线程启动。JVM内部有两种线程:守护线程和非守护线程,main()属于非守护线程,守护线程通常由JVM自己使用,java程序也可以标明自己创建的线程是守护线程。java程序的初始线程 只就是运行main()的线程,这个线程是非守护线程,只要还有任何非守护线程还在运行,那么jvm就存活着。

3.JVM实例的消亡

当程序中的所有非守护线程都终止时,JVM才退出;若安全管理器允许,程序也可以使用java.lang.Runtime类或者java.lang.System.exit()来退出。

3.JVM体系结构

在这里插入图片描述

4.JVM中有哪几种数据类型

JVM中的数据类型分为两类:原始类型和引用类型。既然有类型就会有对应类型的数值:原始值和引用值

1.原始类型

JVM中的原始类型有三类:数值类型、boolean类型和returnAddress类型。

1.数值类型

数值类型就是与我们常用的基本类型,其可以分为两大类:整数类型和浮点类型。

2.boolean类型

boolean类型只有2个值:true和false。编译后使用数字表示:1表示true,0表示false。

3.returnAddress类型

对于 JVM 来说,程序就是存储在方法区的字节码指令,而 returnAddress 类型的值就是指向特定指令内存地址的指针。

JVM支持多线程,每个线程有自己的程序计数器(pc register),而 pc 中的值就是当前指令所在的内存地址,即 returnAddress 类型的数据,当线程执行 native 方法时,pc 中的值为 undefined。

2.引用类型

需要注意引用类型的默认值是null,其表示一个引用不指向任何对象。

1.类类型

Class<A> 可以称之为A类的类型

2.数组类型

3.接口类型

interface和注解都是接口类型

5.一个Java空对象占多少空间

Class文件是一组以8字节为基础单位的二进制流(摘自《深入理解Java虚拟机(周志明)》)。
在这里插入图片描述

  • 在32位系统下,存放Class指针的空间大小是4字节,MarkWord是4字节,对象头为8字节。
  • 在64位系统下,存放Class指针的空间大小是8字节,MarkWord是8字节,对象头为16字节。
  • 64位开启指针压缩的情况下,存放Class指针的空间大小是4字节,MarkWord是8字节,对象头为12字节。
  • 数组长度4字节+数组对象头8字节(对象引用4字节(未开启指针压缩的64位为8字节)+数组markword为4字节(64位未开启指针压缩的为8字节))+对齐4=16字节。

静态属性不算在对象大小内。

这一块的内容还是推荐去看一下《深入理解Java虚拟机》

6.为什么把堆栈分开

  • 从软件设计的角度看
    栈代表了处理逻辑,而堆代表了数据。这样分开,使得处理逻辑更为清晰。分而治之的思想。这种隔离、模块化的思想在软件设计的方方面面都有体现。

  • 堆中的内容可以被多个栈共享
    这种共享的收益是很多的。一方面这种共享提供了一种有效的数据交互方式(如:共享内存),另一方面,堆中的共享常量和缓存可以被所有栈访问,节省了空间。

  • 动态增长
    栈因为运行时的需要,比如保存系统运行的上下文,需要进行地址段的划分。由于栈只能向上增长,因此就会限制住栈存储内容的能力。而堆不同,堆中的对象是可以根据需要动态增长的,因此栈和堆的拆分,使得动态增长成为可能,相应栈中只需记录堆中的一个地址即可。

  • 面向对象就是堆和栈的完美结合
    其实,面向对象方式的程序与以前结构化的程序在执行上没有任何区别。但是,面向对象的引入,使得对待问题的思考方式发生了改变,而更接近于自然方式的思考。当我们把对象拆开,你会发现,对象的属性其实就是数据,存放在堆中;而对象的行为(方法),就是运行逻辑,放在栈中。我们在编写对象的时候,其实即编写了数据结构,也编写的处理数据的逻辑。不得不承认,面向对象的设计,确实很美。

7.什么是栈的起点和程序的起点

程序要运行总是有一个起点的。同C语言一样,java中的Main就是那个起点。无论什么java程序,找到main就找到了程序执行的入口

8.Java中的参数传递时传值呢?还是传引用?

程序运行永远都是在栈中进行的,因而参数传递时,只存在传递基本类型和对象引用的问题。不会直接传对象本身。
Java在方法调用传递参数时,因为没有指针,所以它都是进行传值调用,在运行栈中,基本类型和引用的处理是一样的,都是传值,所以,如果是传引用的方法调用,也同时可以理解为“传引用值”的传值调用,即引用的处理跟基本类型是完全一样的。但是当进入被调用方法时,被传递的这个引用的值,被程序解释(或者查找)到堆中的对象,这个时候才对应到真正的对象。如果此时进行修改,修改的是引用对应的对象,而不是引用本身,即:修改的是堆中的数据。所以这个修改是可以保持的了。

9.对象引用类型分为哪几类

  • 强引用(StrongReference)
    强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。 ps:强引用其实也就是我们平时A a = new A()这个意思。

  • 软引用(SoftReference)
    如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存(下文给出示例)。
    软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。

  • 弱引用(WeakReference)
    弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
    弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

  • 虚引用(PhantomReference)
    “虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

10.Java内存泄漏

在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。
参考:Java的内存泄漏

11.如何解决同时存在的对象创建和对象回收问题

垃圾回收线程GC是回收内存的,而程序运行线程则是消耗和分配内存的,一个回收内存,一个分配内存,从这个点看两者是矛盾的。

  • 从现有的垃圾回收方式中,要进行垃圾回收前,一般都需要暂停整个应用(即:暂停内存分配),然后进行垃圾回收,回收完成后再继续应用。这种方式是最直接的,而且最有效的解决两种矛盾的方式。
    缺点:当堆空间支持增大时,垃圾回收的时间也将相应的持续增大,对应应用暂停的时间也会相应增大。一些相应时间要求很高的应用,比如最大暂停时间要求是好几百秒,那么当堆空间大于几个G时,就很有可能超过这个限制。

  • 使用并发垃圾回收算法,使用这种算法,垃圾回收线程与程序运行线程同时运行,在这种方式下,解决了暂停的问题。
    缺点:因为需要在新生成对象的同时又要回收对象,算法复杂性会大大增加,系统的处理能力也会相应降低,同时“碎片”问题将会比较难以解决。

发布了82 篇原创文章 · 获赞 15 · 访问量 3125

猜你喜欢

转载自blog.csdn.net/qq_34326321/article/details/103578742