java运行时内存

总说java是一次编译,到处运行的语言,也即java的平台无关性。那么,到底是谁帮我们干脏活累活,因为我们知道不管是什么语言,最终都要翻译成底层的机器码才能真正在计算机上运行,那么不同的操作系统,不同的cpu架构,必然导致机器码的不同,是谁这么好心?JRM,Java Runtime Environment,针对不同的操作系统和硬件架构,会有不同的JRM来适配。当我们将自己的java程序在某个平台上运行的时候,会派生出一个JVM,Java Virtual Machine,他实际是JRM的一部分,通过在其中仿真模拟各种计算机功能来实现的用户的意图。所以,真正在使用的时候,了解JVM是java程序员必不可少的技能。

jvm的功能

JVM的输入即为上图的class文件,输出即为对操作系统的硬件的调用。

JVM实例对应了一个独立运行的java程序,它是进程级别 。
a) 启动。启动一个Java程序时,一个JVM实例就产生了,任何一个拥有public static void main(String[] args)函数的class都可以作为JVM实例运行的起点 。

b) 运行。main()作为该程序初始线程的起点,任何其他线程均由该线程启动。JVM内部有两种线程:守护线程和非守护线程,main()属于非守护线程,守护线程通常由JVM自己使用,java程序也可以表明自己创建的线程是守护线程 。
c) 消亡。当程序中的所有非守护线程都终止时,JVM才退出;若安全管理器允许,程序也可以使用Runtime类或者System.exit()来退出 。

在类的具体加载执行的过程中:

首先面临的是加载器对class文件的一系列工作,当发现类文件合法安全有效之后,就可以在运行时环境进行分配,然后具体的执行过程有执行引擎来真正运行。

在java程序员日常讨论中提及的堆和栈,其实都在runtime data area中。

扫描二维码关注公众号,回复: 5602055 查看本文章

如上图,堆和方法区是jvm中所有线程可以共享的,但是java栈,本地方法栈,程序计数器,是线程独享的。

a) 程序计数器(PC寄存器)

由于在JVM中,多线程是通过线程轮流切换来获得CPU执行时间的,因此,在任一具体时刻,一个CPU的内核只会执行一条线程中的指令,因此,为了能够使得每个线程都在线程切换后能够恢复在切 换 之前的程序执行位置,每个线程都需要有自己独立的程序计数器,并且不能互相被干扰,否则就会影响到程序的正常执行次序。因此,可以这么说,程序计数器是每个线程所私有的。由于程序计数器中存储的数据所占空间的大小不会随程序的执行而发生改变,因此,对于程序计数器是不会发生内存溢出现象(OutOfMemory)的。

b) java栈

 Java栈中存放的是一个个的栈帧,每个栈帧对应一个被调用的方法,在栈帧中包括局部变量表(Local Variables)操作数栈(Operand Stack)、指向当前方法所属的类的运行时常量池(运行时常量池的概念在方法区部分会谈到)的引用(Reference to runtime constant pool)、方法返回地址(Return Address)和一些额外的附加信息。当线程执行一个方法时,就会随之创建一个对应的栈帧,并将建立的栈帧压栈。当方法执行完毕之后,便会将栈帧出栈。

c)本地方法栈

本地方法栈与Java栈的作用和原理非常相似。区别只不过是Java栈是为执行Java方法服务的,而本地方法栈则是为执行本地方法(Native Method)服务的

d)堆

Java中的堆是用来存储对象本身的以及数组(数组引用是存放在Java栈中的)。堆是被所有线程共享的,在JVM中只有一个堆。

e)方法区

与堆一样,是被线程共享的区域。在方法区中,存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等。在Class文件中除了类的字段、方法、接口等描述信息外,还有一项信息是常量池,用来存储编译期间生成的字面量和符号引用。在方法区中有一个非常重要的部分就是运行时常量池,它是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来。当然并非Class文件常量池中的内容才能进入运行时常量池,在运行期间也可将新的常量放入运行时常量池中,比如String的intern方法。

方法区可能还有不同的名字,比如永久代,或者元信息空间,其实指的都是方法区。

java虽然解放了程序员对于空间申请释放的繁琐劳动,但如果不了解OOM的种类,也会导致在作业出问题时不知道如何处理。

可以看出,除了程序计数器不会抛出oom外,其他几个部分都会可能导致OOM。

另外,多说一句,我们在用synchronize关键字时,到底同步的是什么,其实就是从主内存(包括java堆和方法区)向子内存(即java栈和native栈)中刷新数据。

 参考:

https://www.cnblogs.com/xing901022/p/7725961.html

https://www.cnblogs.com/fubaizhaizhuren/p/4976839.html

猜你喜欢

转载自www.cnblogs.com/029zz010buct/p/10567781.html