记得刚大学毕业时,为了应付面试,疯狂的在网上刷JAVA的面试题,很多都靠死记硬背。其中有道面试题,给我的印象非常之深刻,有个大厂的面试官,顺着这道题目,一直往下问,问到java虚拟机的知识,最后把我给问住了。

我当时的表情是这样的:

后来我有机会面试别人了,也按照他的思路出面试题,很多已经工作了2年的程序员,结果也和我当年一样,都败在java虚拟机知识上。

我们先看面试题:

String str1 = "hello Alunbar";
String str2 = new String(str1);

会创建几个对象?

网上给出的解释是创建2个对象,str1对象在常量池中,str2对象在堆中。

下面是我和面试官的对话。

面试官:上面的代码创建了几个对象?

我:2个。

面试官:为什么是2个呢?

我:str1对象在常量池中,str2对象在堆中。用“=”等号创建String对象时,会先从字符串常量池中查找是否已经存在字符串对象,存在就直接返回引用地址,否则创建字符串对象并返回引用地址。

面试官:为什么会在常量池中创建字符串对象?

我:。。。我思考了半分钟,尴尬的回答不知道。

面试官:说说jvm虚拟机的内存结构。

我:。。。我再次面露难色,场面一度非常尴尬。

这次面试结束之后,我就回去疯狂查找资料,了解jvm虚拟机的相关知识。

这也是我的第一次面试,给我的印象非常之深刻。

下面我们来说说面试官的两个问题。

1、为什么会在常量池中创建字符串对象。

2、java虚拟机的内存结构。

先来看第一个问题。

为什么会在常量池中创建字符串对象?

字符串在所有编程语言中都是最常用的类型,其他的数据类型都可以转换为字符串类型,像int、long等基本数据类型和String都是可以互相转换的。为了提高字符串的使用效率,jvm虚拟机中特别开辟了一个常量池的内存空间,用于存储基本数据类型的对象,常量池中的对象是可以相互共享的,当然也包括了String。

我们一般将储存字符串的常量池成为字符串常量池。字符串常量池中会存在很多已经创建好的字符串对象,由于String类是用final修饰的,它的值一经创建就不可改变,因此我们不用担心String对象共享而带来程序的混乱。

我们来看一段的代码:

String s1 = "Hello";
String s2 = "Hello";

这段代码只创建一个对象,s1和s2是同一个对象。根据上面的解读,java String s1 = "Hello"这行代码会先在字符串常量池查找Hello对象,没有发现,然后创建Hello对象并将引用返回给s1。java String s2 = "Hello"这行代码,也先去字符串常量池中查找Hello对象,发现已经存在,则直接返回给s2。因此s1和s2是同一个对象。

接着说说使用new创建字符串对象。

通过new创建字符串对象,会在堆中开辟一块新的内存空间,存储String字符串对象,因此使用new方式都会生成新的字符串对象,不管字符串的内容是否一致,使用new创建字符串时存在堆中,堆中的对象会被回收,而使用“=”创建字符串对象,是存放在常量池中,不会被回收,因此建议使用“=”的方式创建字符串对象,避免不必要的java对象创建和销毁的开销。

我们来看下面的创建字符串对象时的内存结构图:

s1和s2是通过“=”创建的字符串对象,它们的内存地址都一样,s3是使用new方式创建的字符串对象,s3和s1、s2的内存地址不一样。

现在接着看第二个问题。

java虚拟机的内存结构

虚拟机内存结构是一个很复杂的问题,这里只能讲一个大概,主要讲各个内存区域的作用。

java虚拟机由类加载器、运行时数据区和执行引擎构成。如下图所示:

平时我们说的java虚拟机内存结构,就是讲运行时数据区。

java虚拟机在执行java程序时,会将内存分为几个区域:程序计数器、方法区、虚拟机栈、本地方法栈、堆。

其中,方法区和堆是线程共享,程序计数器、虚拟机栈、本地方法栈时线程不共享。

1、程序计数器

只要学过汇编语言,对这个程序计数器都好理解,就是记录下一条将要执行的字节码指令。

通过操作系统知识我们知道启动一个程序时,就会创建一个进程,因此在执行java程序时,就会创建一个进程,java虚拟机就是一个进程。

一个进程中由多个线程组成,在任何一个时刻,java虚拟机只能执行一条线程中的指令。

java虚拟机通过读取某一个线程中的程序计数器决定该线程需要执行哪个基础功能,例如循环、读取数据库、跳转、异常处理、线程恢复等。

因此每个线程的程序计数器是相互独立,互不影响的。

2、java虚拟机栈

就是我们常说的java栈,在执行方法时,会在java栈中创建一个栈帧,用于存储局部变量表、操作数栈、方法出口等信息。

局部变量表中又会存放执行方法需要的boolean、char等各种基本数据类型,对象引用等。局部变量表大小在代码编译期间就已经确定。java栈也是线程私有。

创建线程时同步创建java栈,线程结束,java栈也同时销毁,释放占用的内存。

3、本地方法栈

和java虚拟机栈功能类似,有的虚拟机会将java虚拟机栈和本地方法栈合并。本地方法栈主要为虚拟机执行Native方法提供服务。

4、java堆

虚拟机中最大的一块内存区域,虚拟机启动时创建,主要用于存放对象实例,这块内存区域由所有线程共享。这个区域内的对象,可以被所有的线程访问。

这个区域也是java虚拟机重点管理的对象,当这块区域中的对象没有被引用,达到回收标准时,就会被java垃圾收集器回收,释放占用的内容空间。

java堆分为新生代和老年代,新生代又分为Eden空间、From Survivor空间和To Survivor空间。

使用new操作创建对象时,就会在这个区域开辟一块内存用于存储对象。

上面提到的java String str1 = new String("Hello")创建字符串,就会在java堆中开辟一块内存用于存储str1对象。

5、方法区

方法区主要存储被虚拟机加载的类信息、常量、静态变量等数据,我们也将这个内存区域称为永久代,这个区域不会进行内存回收。

方法区和java堆一样,所有线程共享。

方法区中包含一个运行时常量池,上面提到的java String str = "Hello"创建字符串,就是在运行时常量池中创建“Hello”对象。

小结:

1、两种创建字符串对象的差异。

2、java虚拟机内存区域的作用。

从一道面试题深入了解java虚拟机内存结构的更多相关文章

  1. JVM基础系列第6讲:Java 虚拟机内存结构

    看到这里,我相信大家对于一个 Java 源文件是如何变成字节码文件,以及字节码文件的含义已经非常清楚了.那么接下来就是让 Java 虚拟机运行字节码文件,从而得出我们最终想要的结果了.在这个过程中,J ...

  2. 深入剖析Java虚拟机内存结构

    深入剖析Java虚拟机内存模型 JVM整体架构 JVM整体架构如下: 通过编写代码来分析整个内存区域 public class Math { public static final Integer C ...

  3. Java虚拟机内存模型及垃圾回收监控调优

    Java虚拟机内存模型及垃圾回收监控调优 如果你想理解Java垃圾回收如果工作,那么理解JVM的内存模型就显的非常重要.今天我们就来看看JVM内存的各不同部分及如果监控和实现垃圾回收调优. JVM内存 ...

  4. Java程序猿从笨鸟到菜鸟之(九十二)深入java虚拟机(一)——java虚拟机底层结构具体解释

    本文来自:曹胜欢博客专栏.转载请注明出处:http://blog.csdn.net/csh624366188 在曾经的博客里面,我们介绍了在java领域中大部分的知识点,从最基础的java最基本的语法 ...

  5. 如何设置Java虚拟机内存以适应大程序的装载

    Java虚拟机对于运行时的程序所占内存是有限制的,当我们的项目或者程序很大时,往往会照成内存溢出. 举个例子: public class SmallTest1 { public static void ...

  6. 打包apk java 虚拟机内存不足

    解决方案:在android->sdk->build-tools-android-version 中有个 dx.bat dx.bat --dex 命令的dx.bat脚本有这样一句代码 REM ...

  7. Java虚拟机-内存tips

    java虚拟机内存可以分为独占区和共享区. 独占区:虚拟内存栈.本地方法栈.程序计数器. 共享区:方法区.Java堆(用来存放对象实例). 程序计数器 比较小的内存空间,当前线程所执行的字节码的行号指 ...

  8. Java虚拟机内存溢出异常--《深入理解Java虚拟机》学习笔记及个人理解(三)

    Java虚拟机内存溢出异常--<深入理解Java虚拟机>学习笔记及个人理解(三) 书上P39 1. 堆内存溢出 不断地创建对象, 而且保证创建的这些对象不会被回收即可(让GC Root可达 ...

  9. java虚拟机内存不足,“Could not create the Java Virtual Machine”问题解决方案

    java虚拟机内存不足,"Could not create the Java Virtual Machine"问题解决方案 在运行java程序时,遇到问题"Could n ...

随机推荐

  1. 使用GPU跑Tensorflow代码实录

    使用conda创建一个新的虚拟环境 输入 conda create -n intelligent-judge python=3.6 创建一个python版本为3.6的名字是intelligent-ju ...

  2. 小白专场-多项式乘法与加法运算-c语言实现

    目录 一.题意理解 二.求解思路 三.多项式的表示 3.1 数组 3.2 链表 四.程序框架搭建 五.如何读入多项式 六.如何将两个多项式相加 七.如何将两个多项式相乘 八.如何将多项式输出 一.题意 ...

  3. d3.js 教程 模仿echarts legend功能

    上一节记录没有加上echarts的legend功能,这一小节补一下. 1. 数据 我们可以从echarts中看出,折线数据并不是我们传进入的原始数据(多数情况下我们也不会修改原始数据),而是原始数组的 ...

  4. HDU-6053 TrickGCD

    题目连接: https://vjudge.net/problem/HDU-6053 Description You are given an array A , and Zhu wants to kn ...

  5. UVA - 10480 Sabotage 最小割,输出割法

    UVA - 10480 Sabotage 题意:现在有n个城市,m条路,现在要把整个图分成2部分,编号1,2的城市分成在一部分中,拆开每条路都需要花费,现在问达成目标的花费最少要隔开那几条路. 题解: ...

  6. 背包形动态规划 fjutoj2347 采药

    采药 TimeLimit:1000MS  MemoryLimit:128MB 64-bit integer IO format:%lld   Problem Description 辰辰是个天资聪颖的 ...

  7. 201871010134-周英杰《面向对象程序设计(java)》第一周学习总结

    项目 内容 java https://www.cnblogs.com/nwnu-daizh/ 任课老师博客下 https://www.cnblogs.com/nwnu-daizh/p/11435127 ...

  8. pyppeteer的使用

    pyppeteer的使用 安装 属于第三方模块进行安装. pip install pyppeteer 在Linux中,如果权限不够则加上. sudo pip install pyppeteer 使用 ...

  9. 单细胞转录组测序数据的可变剪接(alternative splicing)分析方法总结

    可变剪接(alternative splicing),在真核生物中是一种非常基本的生物学事件.即基因转录后,先产生初始RNA或称作RNA前体,然后再通过可变剪接方式,选择性的把不同的外显子进行重连,从 ...

  10. c语言实现名值对通过key查找value

    需求.例如: 1." key1 = value1 " 通过"key1"从该字符串中查找出"value",value去除前后空格 2.&quo ...