JVM理论与实践【内存结构】

时间:2022-04-21 00:03:12

学Java三年有余,对自己来说JVM一直以来都是黑匣子,看不懂,摸不透。JVM理论与实践【内存结构】作为一个有技术情节、略带些许完美主义情节的攻城狮,不了解JVM似乎有时候夜不能寐,总觉得有一个未知的世界自己需要探索。JVM理论与实践【内存结构】理论为实践服务,学习JVM不是因为它好玩,其实一点都不好玩,只是因为它有用罢了。实用主义者总比快餐主义让人踏实。 好吧,好奇心害死猫(Curiosity kills the cat),中秋佳节还面对电脑,真是罪过。闲话不多说了,否则显得自己很没礼貌。

       学习JVM原理之前,只知道JVM的内存结构有堆、栈之分,堆中存放对象,栈中存放对象的引用。事实上,情况远比上面的一句话复杂的多,当然我仍然没忘记自己是实用主义者,因为我实在想不出有什么理由,去花费大把的时间去和JVM纠缠。JVM理论与实践【内存结构】

 

【JVM的内存结构】

       简单起见,可以将JVM的内存结构划分为Java堆、虚拟机栈、方法区、本地方法栈、运行时常量池和直接内存。这样的划分不见得合理、或者逻辑清晰,只是为了学习方便而已。如下图所示:

JVM理论与实践【内存结构】
 1. Java堆:Heap

    是运行时的数据区,主要用来存放Java的对象、数组内容。

 2. 虚拟机栈:Virtual Machine Stacks

    也可以称之为线程栈,是线程运行的内存区域,存放对象的引用(对象在堆中存放的内存地址),通过对象的引用来访问对象或者数组。

 3.  方法区:Method AreaJVM理论与实践【内存结构】

    用来存放被虚拟机加载的类的相关信息(如类中的字段、方法的签名等)以及一些其他的元数据。方法区   在JVM规范中被描述为堆(Heap)的一个逻辑组成部分,但是通常称之为非堆(Non-Heap)内存,以表明其和     用来存放对象、数组的堆的不同。在Sun公司提供的HotSpot虚拟机中,方法区使用Java堆(Heap)中的永久代(Permanent Generation)来进行存放。

 4. 本地方法栈:Native Method Stacks

    用于运行本地方法(Native method)的内存区域,在Sun公司的HotSpot虚拟机中本地方法栈、线程栈共用一块内存区域。

 5. 运行时常量池:Runtime Constant PoolJVM理论与实践【内存结构】

    存放运行时的产生的大量常量,如字符串常量,存放在如上所述的方法区。

 6. 直接内存:Direct MemoryJVM理论与实践【内存结构】

    JDK1.4中加入的NIO(非阻塞I/O)操作类、引入了基于通道(Channel)和缓冲(Buffer)的I/O方式,通过使用Native函数分配堆外内存,然后使用DirectByteBuffer对象来操作这块内存。直接内存和Java堆内存的分配没有任何交集,但是堆内存大小的设置会间接直接内存的可用空间。

 

【应用实例】

 1. 产生Java堆溢出:通过创建大量的对象,并且保持引用来实现。

     定义大对象: JVM理论与实践【内存结构】

Java代码  JVM理论与实践【内存结构】
  1. class MyObject {  
  2.     byte[] buffer = new byte [1024 * 1024];   
  3. }  

    创建对象,并添加到List集合以避免被垃圾回收器回收: 

Java代码  JVM理论与实践【内存结构】
  1. List<MyObject> bufferList = new ArrayList<MyObject>();  
  2. int i = 0;  
  3. while(true) {  
  4.     System.out.println("第" + (i++ + 1) + "次添加对象.");  
  5.     bufferList.add(new MyObject());  
  6. }  

     设置虚拟机参数: 

       右键Run As快捷菜单,选择“Run Configurations...”

       切换到Arguments选项卡,在VM Arguments文本框输入参数      JVM理论与实践【内存结构】     

Shell代码  JVM理论与实践【内存结构】
  1. -Xms20m -Xmx20m  

        运行程序,抛出Java堆内存溢出异常:

          java.lang.OutOfMemoryError: Java heap space

  2. 线程栈溢出:通过大量的递归调用来消耗线程栈内存。

    定义递归调用函数:JVM理论与实践【内存结构】

Java代码  JVM理论与实践【内存结构】
  1. private int i =0;  
  2.   
  3. private void recursive() {  
  4.     System.out.println(++ i);  
  5.     recursive();  
  6. }  

     设置VM Arguments参数:

Shell代码  JVM理论与实践【内存结构】
  1. -Xss128k  

     运行该程序抛出栈溢出异常:JVM理论与实践【内存结构】

         java.lang.*Error

 

============================

 Ok,JVM内存结构学习到此结束,接下来该看看垃圾回收GC啦!