面试总结:java程序执行过程 + JVM内存管理 + GC垃圾回收机制

本文主要是介绍面试总结:java程序执行过程 + JVM内存管理 + GC垃圾回收机制,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

java程序执行过程 +JVM内存管理 + GC垃圾回收机制

1、Java程序执行过程

一个java程序的编译和执行过程如下:

  • .java ——编译——> .class
  • 类加载器负责加载各个字节码文件(.class)
  • 加载完.class后,由执行引擎执行,在执行过程中,需要运行时数据区提供数据

这里写图片描述


补充:手动编译.java

Main.java

public class Main {public static void main(String[] args) {System.out.println("asdf");}
}

使用命令javac Main.java编译,编译后能看到一个.class文件,里面长这样

cafe babe:前四个字节称为魔数,作用是标识不同版本的虚拟机
0000 0034:是java的版本号,0000是次版本号,0034是主版本号

  • 0x0034 = 3 * 16 + 4 = 52 表示JDK1。8版本

0013:是常量池入口,0x0013 = 19,表示常量池有18项常量(1~18,第0项空出来)

cafe babe 0000 0034 001d 0a00 0600 0f09
0010 0011 0800 120a 0013 0014 0700 1507
0016 0100 063c 696e 6974 3e01 0003 2829
5601 0004 436f 6465 0100 0f4c 696e 654e
756d 6265 7254 6162 6c65 0100 046d 6169
6e01 0016 285b 4c6a 6176 612f 6c61 6e67
2f53 7472 696e 673b 2956 0100 0a53 6f75
7263 6546 696c 6501 0009 4d61 696e 2e6a
...

最后使用命令java Main运行刚才的程序

关于.class文件的分析,请见:http://www.cnblogs.com/winner-0715/p/4935256.html



2、JVM内存管理

JVM将内存划分为6个部分:PC寄存器(也叫程序计数器)、虚拟机栈、堆、方法区、运行时常量池、本地方法栈

这里写图片描述


  • PC寄存器(程序计数器):用于记录当前线程运行时的位置,每一个线程都有一个独立的程序计数器,线程的阻塞、恢复、挂起等一系列操作都需要程序计数器的参与,因此必须是线程私有的。

  • java 虚拟机栈:在创建线程时创建的,用来存储栈帧,因此也是线程私有的。java程序中的方法在执行时,会创建一个栈帧,用于存储方法运行时的临时数据和中间结果,包括局部变量表、操作数栈、动态链接、方法出口等信息。这些栈帧就存储在栈中。如果栈深度大于虚拟机允许的最大深度,则抛出StackOverflowError异常。

    • 局部变量表:方法的局部变量列表,在编译时就写入了class文件
    • 操作数栈:int x = 1; 就需要将 1 压入操作数栈,再将 1 赋值给变量x
  • java 堆:java堆被所有线程共享,堆的主要作用就是存储对象。如果堆空间不够,但扩展时又不能申请到足够的内存时,则抛出OutOfMemoryError异常。

    StackOverflowErrorOutOfMemoryError
    java栈java堆
    栈深度超过范围了(比如:递归层数太多了)内存空间不够了(需要及时释放内存)

vio_dzyls:栈也会报OOM,当扩展栈的时候无法申请足够的内存空间时

 <br/>
  • 方法区:方发区被各个线程共享,用于存储静态变量、运行时常量池等信息。
  • 本地方法栈:本地方法栈的主要作用就是支持native方法,比如在java中调用C/C++

3、GC回收机制

  • 哪些内存需要回收?——who
  • 什么时候回收?——when
  • 怎么回收?——how

1、哪些内存需要回收?

  • java堆、方法区的内存
线程私有线程共享
程序计数器、虚拟机栈、本地方法栈java堆、方法区
随线程生而生,随线程去而去。线程分配多少内存都是有数的,当线程销毁时,内存就被释放了堆和方法区的内存都是动态分配的(使用new关键字),所以也需要动态回收。
这部分内存的回收依赖GC完成

2、什么时候回收?

  • 引用计数法
  • 可达性分析

(1)、引用计数法

给对象添加一个引用计数器,每当有一个地方引用它时,计数器加一。反之每当一个引用失效时,计数器减一。当计数器为0时,则表示对象不被引用。

举个例子:

Object a = new Object(); // a的引用计数为1
a = null; // a的引用计数为0,等待GC回收

但是,引用计数法不能解决对象之间的循环引用,见下例

Object a = new Object(); // a的引用计数为1
Object b = new Object(); // b的引用计数为1a.next = b; // a的引用计数为2
b.next = a; // b的引用计数为2a = null; // a的引用计数为1,尽管已经显示地将a赋值为null,但是由于引用计数为1,GC无法回收a
b = null; // b的引用计数为1,同理,GC也不回收b

(2)、可达性分析

设立若干根对象(GC Root),每个对象都是一个子节点,当一个对象找不到根时,就认为该对象不可达。

这里写图片描述

没有一条从根到Object4 和 Object5的路径,说明这两个对象到根是不可达的,可以被回收

补充:java中,可以作为GC Roots的对象包括:

  • java虚拟机栈中引用的对象
  • 方法区中静态变量引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中引用的对象

小何117KNO: JVM虚拟机不采用引用计数法,只通过可达性分析进行垃圾回收


3、怎么回收?

  • 标记——清除算法
  • 复制算法
  • 分代算法

(1)、标记——清除算法

遍历所有的GC Root,分别标记处可达的对象和不可达的对象,然后将不可达的对象回收。

缺点是:效率低、回收得到的空间不连续

(2)、复制算法

将内存分为两块,每次只使用一块。当这一块内存满了,就将还存活的对象复制到另一块上,并且严格按照内存地址排列,然后把已使用的那块内存统一回收。

优点是:能够得到连续的内存空间
缺点是:浪费了一半内存

这里写图片描述

(3)、分代算法

在java中,把内存中的对象按生命长短分为:

  • 新生代:活不了多久就go die 了,比如局部变量
  • 老年代:老不死的,活的久但也会go die,比如一些生命周期长的对象
  • 永久代:千年王八万年龟,不死,比如加载的class信息

有一点需要注意:新生代和老年代存储在java虚拟机堆上 ;永久代存储在方法区上

回收方法
新生代使用复制算法
老年代使用标记——清除算法
永久代————————


补充:java finalize()方法:

在被GC回收前,可以做一些操作,比如释放资源。有点像析构函数,但是一个对象只能调用一次finalize()方法。


注:楼主纯小白一枚,有错误还请指出,谢谢 ~

这里写图片描述

这篇关于面试总结:java程序执行过程 + JVM内存管理 + GC垃圾回收机制的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/989228

相关文章

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

Spring AI集成DeepSeek的详细步骤

《SpringAI集成DeepSeek的详细步骤》DeepSeek作为一款卓越的国产AI模型,越来越多的公司考虑在自己的应用中集成,对于Java应用来说,我们可以借助SpringAI集成DeepSe... 目录DeepSeek 介绍Spring AI 是什么?1、环境准备2、构建项目2.1、pom依赖2.2