【java面试经(架构师&设计师)-第3课】java虚拟机和内存优化
发布日期:2021-05-10 03:44:40 浏览次数:22 分类:精选文章

本文共 2418 字,大约阅读时间需要 8 分钟。

技术解析

一、JDK、JRE、JVM的概念和区别

JDK(Java Development Kit,Java开发工具包)是Java开发的核心软件包,包含JRE和一系列实用工具如Java Compiler( javac.exe)、Java Runtime Environment(jre)、Jar 包署装工具(jar.exe)等。

J2SE(Standard Edition)则是JDK中的一种标准版本,提供默认的开发环境配置。

JVM(Java Virtual Machine,本质是指Java Runtime Environment,即JRE)是Java程序在执行时所依赖的虚拟运行环境,它负责将编写的Java代码转换为机器操作码并执行。这一环境模拟了 Javier机器,因此在底层操作系统上运行Java程序实际上是在运行JVM。

在理论上,JVM是Java程序运行的核心虚拟机,它不仅要处理上述代码转换工作,还需要负责类的加载与卸载。

Java程序的执行过程遵循以下一般流程:首先代码在开发者工作站的JVM(即JDK的一部分,称为Development Virtual Machine,DVM)中被编译并转换成Java字节码。然后这个字节码再被上线到目标服务器的JVM(JRE)中运行。

JVM如何加载字节码文件?这是通过类加载器(Class Loader)这一机制实现的。每个JVM都有自己的类加载器,负责将字节码文件加载进来。一旦确认字节码的来源是可信的,类加载器会将它解码器转化为内存中的机器码。这类似于操作系统中不同程序的模块如何被动态加载与卸载的过程。

关于类的卸载,这是由类路径加载器来执行的。当一个类已经加载到JVM中,而后续版本的类文件被发布后,旧的版本需要被移除以节省内存并更新代码。但是,在大多数现代应用中,JVM会采用懒刷新机制,这使得卸载操作在不影响程序的正常运行的情况下进行。

二、桌面上,JVM的面积和存储状态有多大?具体来说,一个对象((具有两个属性和四个方法))被实例化100次时,内存中的存储状态是怎样的?

在深入探讨JVM内存模型之前,我们需要明确几个概念:堆("Heap"), 栈("Stack"), 方法区("Method Area"), 和本地方法区(" Native Method Area")。这些区域共同构成JVM的内存模型。

在Java运行时数据区,对象存放在堆中,而数组、字符串等则存放在堆中。这里的每个对象其实占用三个部分:真实的对象实例、属性字段以及方法相关信息。例如,一个对象有两个属性和四个方法,它们在内存中各自占用不同的空间,全部加起来就是每个对象在内存中所占的空间。

如果一个对象被实例化100次,那么在堆中总共有100个这样的对象存储区域,每个对象里的属性数目、方法数目也与100相关联。

当一个对象被创建后,就会按照预定的内存分配策略放置在栈还是堆中的关键在于这个对象的生命周期。栈主要用于存储局部变量、方法参数和返回值,它的内存管理很有特点:当一个线程离开方法的时候,栈自动释放这些内存资源。而堆的内存管理则更加智能,可以根据需要进行扩展和收缩。

为什么在Java的内存分配策略中存在这样的设计?这是因为栈和堆的设计理念要追求的目标是提高程序的运行效率和内存的利用率。栈是一种更加简单有效的内存管理机制,适用于小块的、需要快速释放的资源。而堆适合处理大块且需要长期留存的对象资源。

细化到具体实现,程序在运行的时候会根据对象的类型和引用情况来决定放置栈还是堆中的位置。

三、堆和栈的主要区别是什么?在什么情况下程序会将内存分配到堆,什么时候分配到栈?

堆和栈都是JVM中的关键内存区域,它们的主要区别体现在内存管理方式和使用场景上。在内存管理方式上,栈采用"先进后出"(LIFO)的方式,内存释放机制比较简单快捷。堆的内存管理相对复杂,通常会采用更多的算法来进行垃圾回收。区别在于使用对象的生命周期,如果一个对象的生命周期短(如局部变量),它更适合在栈上分配,因为它的使用范围有限,释放比较简单。反之,对象的生命周期较长(如实例对象),就需要通过堆内存来持久化存储。

鉴于这一点来说,在具体的内存分配时,"谁用谁分配"的大决策会受到严格的对象创建方式的限制。比如,我可以借助"Unsafe"这个类(但是要注意这种记法可能已经被去除),我可以强行指定内存分配的区域。但在大多数情况下,内存分配的策略是由JVM的内存管理系统自动决定的。

在这种设计下,如何处理不同对象的内存需求,而不会造成内存泄漏或内存碎片的主要问题,就需要依赖JVM的分代收集机制。

这一设计理念之一源自于JVM的诞生背景。最初的理解类似于操作系统的内存管理机制,为了不同的资源级别设计不同的内存管理方式。这使得用户可以更高效地配置内存,避免了大量跨跨的内存碎片问题。

继续谈及GC(垃圾回收机制),它是用来管理那些不再被引用死活对象的。目前主要使用的GC算法有以下几种:复制算法、标记-清除算法、标记-整理算法以及暂时复制算法等。每一种算法都有不同的优缺点,适用于不同的场景。比如,标记-清除算法简单实现但会引起空间浪费;而复制算法更适合处理较小的内存空间,效率较高,但也需要更多的内存资源。

在实际GC过程中,由于存在内存碎片的问题,full GC(全旧集合)则会被触发。这可能导致性能上的性能下降,尤其是在隐含地需要处理大量对象被标记和清除的时候。

系统调用System.gc()也是一个常见操作,该方法可以让JVM进行一次主动的内存回收操作,适用于希望主动释放内存并减少GC压力的场景。正确使用这样的方法可以优化程序性能,但应尽量避免在性能敏感的代码路径上使用,以免造成内存管理上的循环调用问题。

总的来说,JVM作为一个复杂而精密的系统,内存管理的策略体现了性能与开发便利性的双重考量。

上一篇:【java面试经(架构师&设计师)-第4课】java基础常识
下一篇:【java面试经(架构师&设计师)-第2课】java常用数据结构(二)

发表评论

最新留言

表示我来过!
[***.240.166.169]2025年05月09日 23时18分37秒