欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

【JAVA进阶架构师指南】之二:JVM篇

程序员文章站 2022-04-15 13:41:01
前言 谈到JAVA,就不得不提JVM JAVA程序员绕不开的话题.也许有童鞋会说,我不懂JVM,但是我一样可以写出JAVA代码,我相信说这种话的童鞋,往往是只有1 3年的初级开发人员,对JAVA理解还不深,不明白JVM的重要性,那接下来我们来说说,为什么要学习JVM? 1.理解JVM,才能帮助我们写 ......

前言

  谈到java,就不得不提jvm---java程序员绕不开的话题.也许有童鞋会说,我不懂jvm,但是我一样可以写出java代码,我相信说这种话的童鞋,往往是只有1-3年的初级开发人员,对java理解还不深,不明白jvm的重要性,那接下来我们来说说,为什么要学习jvm?
  1.理解jvm,才能帮助我们写出更好,更健壮的代码.举个例子,以下代码的执行结果会是什么呢?很多童鞋肯定会说:嗯?当我傻吗?两个不都是true吗?这有啥好说的,真的是这样吗?感兴趣的童鞋可以自己下来试一试,至于为什么是这样的结果,在下文会解释清楚.
【JAVA进阶架构师指南】之二:JVM篇
  2.理解jvm,可以帮助我们提升java程序的性能,排除问题.
  3.也是最重要的一点,面试必问!

虚拟机的种类

  我们知道,目前使用范围最广的虚拟机是sun公司的hotspot vm,在这之前,sun公司发布的第一款虚拟机是sun classic/exact vm,这是世界上第一款商用虚拟机.另外其他公司也有自己的虚拟机,比如ibm j9 vm,google android dalvik vm,apache harmony,microsoft vm等待,但是使用范围最广的还是hotspot.

jvm内存划分

  引用一张图来说明:
【JAVA进阶架构师指南】之二:JVM篇

  可以看到,jvm主要由方法区/堆区/虚拟机栈/本地方法栈/程序计数器五个部分组成,从线程的角度来看,分为线程公有的部分(方发区/java堆)和线程私有的部分(虚拟机栈/本地方法栈/程序计数器).

方法区

  存放已经被虚拟机加载的[类信息/常量/静态变量/即时编译后的代码]等,有些文章也称方法区为永久代,主要发生的异常是内存溢出:outofmemoryerror.另外在jdk1.6版本中,常量池(这里特指运行时常量池,我们一般说的常量池也都是指的运行时常量池)是存放于方法区中的(因此方法区可能会经常内存溢出),jdk1.7的时候常量池移到了java堆(heap)中,在jdk1.8的时候,已经没有方法区了,取而代之的是一块叫元数据(metaspace)的空间.

java堆

  java堆主要存放的是对象实例以及数组等信息,主要发生的异常仍然是内存溢出:outofmemoryerror.并且java堆区是gc重点关注的区域.另外,我们常说,几乎所有的对象分配内存都是在java堆中进行,而不是说所有对象100%都在java堆中分配内存,是因为有两种例外情况不会在java堆中分配内存,第一种是tlab(线程本机分配缓存),另一种是栈上分配,既然想成为一名架构师,童鞋们应该要弄明白什么是tlab和栈上分配,发挥你们的能力,尽情google吧.

虚拟机栈

  java方法执行的内存模型,每个方法在执行的时候会封装成一个栈帧,存放[局部变量表/操作数栈/动态链表/方法出口]等信息,方法的执行对应栈帧入栈和出栈的过程.栈的深度是有大小的,默认情况下栈的内存为1m,因此虚拟机栈除了发生内存溢出异常,还有可能发生*error异常.

本机方法栈

  和虚拟机栈作用类似,区别在于本地方法栈保存的是native方法的信息.

程序计数器

  当前线程执行的字节码行号指示器,是jvm中唯一一块没有内存溢出异常的区域.

常量池

  接下来我们再倒回来看看,文章开头的代码,执行结果会是什么:
【JAVA进阶架构师指南】之二:JVM篇
  127返回的是true,128返回的确是false.为什么?
  首先我们知道,在java语言中 == 比较的是两个对象的内存地址,只有equals方法才是比较两个对象是否相等,执行结果告诉我们,值都为127的integer a和b内存地址是相同的,他们是同一个对象,而值为128的integer c和d的内存地址不同,他们是不同的两个对象,那为什么127就是相同的对象,128就是不同的对象呢?还记得上文中,我们说方法区中有一块区域叫运行时常量池,存放的是各种常量,java语言对byte/short/char/int/string设置了常量池,比如我们查看integer的源码:
【JAVA进阶架构师指南】之二:JVM篇
  可以发现,integer的常量池范围是-128~127,在该范围内的integer对象都会复用常量池中的值,因此a和b是相同对象,而超过该范围,会重新new一个新的对象,因此c和d都是重新new出来的,地址当然不同,因此是false.另外string类型的常量池和前面四种类型不一样,string类型的常量池是通过final来实现的.而float/double没有常量池的概念,因为float和double本身都是科学技术法表示近似数,无法精确计算,存在精度丢失的情况,因此没法为float和double创建常量池.

  本文我们了解了jvm的内存区域,下一篇文章,让我们来学习类加载机制,敬请期待!

  如果觉得博主写的不错,欢迎关注博主微信公众号,博主会不定期分享技术干货!
  【JAVA进阶架构师指南】之二:JVM篇

本文由博客一文多发平台 openwrite 发布!

上一篇: 100kg不好看

下一篇: 想*咋的