深入理解JVM(DAY 1)

JVM(Java Virtural Machine) JAVA虚拟机的存在使Java开发的程序具有跨平台性,即一次编译处处运行。在编译完成后生成.class文件(字节码文件)提供给JVM运行。

JVM在运行时的数据区如下:

    

 

接下来让我们一起学习一下JVM运行时内存空间的分配

 

一:程序计数器

  程序计数器: 属于每个线程自身,它的主要作用是通过自身值的改变选取下一条字节码指令。此外在分支、循环、跳转、异常处理以及线程恢复等功能都需要它来处理。注:(在调用native方法时程序计数器中的值为空),Java虚拟机通过线程轮流切换的形式完成多线程的实现,在多核cpu中每一个内核在同一时刻只会执行一个线程中的一条指令。不同的线程在切换后需要当前执行的位置,此时就需要程序计数器来标记切换前的执行位置,从而占用cpu资源接着此前未执行的位置往下执行。

 

二:Java虚拟机栈与本地方法栈

   java虚拟机在编译完成后,就会分配虚拟机栈(局部变量表)的内存空间,在方法的运行期间不会改变局部变量表的大小。如果在线程执行中请求的栈深度大于虚拟机栈已经被分配的深度,则抛出Stack OverflowError异常;若此虚拟机允许动态扩展,则此虚拟机会进行扩展,直到无法申请到足够的内存时抛出 OutofMemoryError异常。而本地方法栈与java虚拟机栈的区别在于:java虚拟机栈为java方法服务,而本地方法栈为虚拟机所用的native方法服务。不同的java虚拟机有着不同的实现方法,在Hotspot虚拟机中将本地方法栈与java虚拟机栈合二为一。

 

三:Java堆内存

  堆内存:java堆在虚拟机启动时创建,它的大小是虚拟机管理的内存空间中最大的。它存在的目的是存放对象实例与数组,几乎所有的对象实例与数组都会被存放在堆内存中。为了使堆内存不被对象塞满抛出异常,java堆是GC经常光顾的场所。堆内可以分为:新生代与老年代,再细分一下存在Eden区、To Survivor区、From Survivor区等。

 
  控制JVM堆大小的参数:    -Xms 最小堆的大小, 也就是当你的虚拟机启动后, 就会分配这么大的堆内存给你
                                          -Xmx 是最大堆的大小
         [当最小堆占满后,会尝试进行GC,如果GC之后还不能得到足够的内存(GC未必会收集到所有当前可用内存),分配新的对象,那么就会扩展堆,如果-Xmx设置的太小,扩展堆就会失败,导致OutOfMemoryError错误提示。]
 
四:方法区
  方法区:各个线程共享,用来存储已被虚拟机加载的属于类的信息、常量、静态变量、即时编译器编译后的代码等数据。因为HotSpot团队使用永久代来实现方法区导致其大部分数据无法被GC,但是对于IBM J9等虚拟机不存在永久代的概念。目前来讲Hotspot本身也将放弃永久代并搬家至 Native Memory实现方法区的规划。 此区域除了和堆一样不需要连续的内存空间和可扩容与固定空间大小外,还可以选择不实现垃圾回收。 在此区域的垃圾回收针对常量池的回收与类型的卸载,方法区在无法满足内存分配需求时抛出OutofMemoryError异常。  
  【关于类卸载:通常在满足以下三个条件时即可发生类的卸载,完成一个类的整个生命周期:
                               一:该类的所有实例都已经被回收,即java堆中不存在任何该类的实例;
                                   二:该类的类加载器(classLoader)已经被回收;
                              三: 该类的Class对象在任何地方都没有被引用或者没有被通过反射调用。



五:运行时常量池
  运行时常量池作为方法区的一部分,用于存放编译器生成的各种字面量和符号引用,这部分会在类加载后存放到方法区的运行时常量池中。一个Class文件类的版本、字段、方法、接口等信息外还有一个就是常量池。
不同的厂商会实现不同格式的常量池,一般来说还会包含翻译出来的直接引用。常量不仅会在程序编译时进入常量池,还被允许在运行时将新的常量放入池中,例如String的intern()方法。

 
 
最后谢谢大家的观看,每天我都会更新自己对JVM的理解与学习!

 

 

 

 

   

posted @ 2021-12-21 22:08  11今天练腿了嘛?  阅读(37)  评论(0)    收藏  举报