加入收藏 | 设为首页 | 会员中心 | 我要投稿 PHP编程网 - 黄冈站长网 (http://www.0713zz.com/)- 数据应用、建站、人体识别、智能机器人、语音技术!
当前位置: 首页 > 综合聚焦 > 资源网站 > 空间 > 正文

浅谈虚拟机内存区

发布时间:2019-04-10 13:46:24 所属栏目:空间 来源:Wooola
导读:副标题#e# 1. Java 虚拟机内存区概述 我们在编写程序时,经常会遇到OOM(out of Memory)以及内存泄漏等问题。为了避免出现这些问题,我们首先必须对JVM的内存划分有个具体的认识。JVM将内存主要划分为:方法区、虚拟机栈、本地方法栈、堆、程序计数器。 2. J
副标题[/!--empirenews.page--]

 1. Java 虚拟机内存区概述

我们在编写程序时,经常会遇到OOM(out of Memory)以及内存泄漏等问题。为了避免出现这些问题,我们首先必须对JVM的内存划分有个具体的认识。JVM将内存主要划分为:方法区、虚拟机栈、本地方法栈、堆、程序计数器。

2. Java 虚拟机运行时数据区

2.1. 运行时数据区划分

2.1.1 运行时数据区图

浅谈虚拟机内存区

2.1.2 运行时数据区包括

  • 方法区(Method Area)
  • 虚拟机栈(VM Stack)
  • 本地方法栈(Native Method Stack)
  • 堆(Heap)
  • 程序计算器(Program Counter Register)

2.2. 方法区(Method Area)

2.2.1 方法区的概念

方法区又叫静态区,存放的是已加载的类的基本信息、常量、静态变量等。它是各个线程共享区域。

比方说我们在写Java代码时,每个线程度可以访问同一个类的静态变量对象。由于使用反射机制的原因,虚拟机很难推测哪那个类信息不再使用,因此这块区域的回收很难。

2.2.1.1 静态块和非静态块有什么区别?

  • 类(Class)和对象(Object)的区别与联系?
  • 为什么静态块中不能使用this、super关键字?
  • 为什么java的静态方法可以直接用类名调用?

2.2.2 方法区的特点

线程间共享区域

2.2.3 方法区的异常

对这块区域主要是针对常量池回收,值得注意的是JDK1.7已经把常量池转移到堆里面了。同样,当方法区无法满足内存分配需求时,会抛出OutOfMemoryError。制造方法区内存溢出,注意,必须在JDK1.6及之前版本才会导致方法区溢出,原因后面解释,执行之前,可以把虚拟机的参数-XXpermSize和-XX:MaxPermSize限制方法区大小。

代码清单如下:

  1. public static void printOOM() { 
  2.  List<String> list = new ArrayList<String>(); 
  3.  int i = 0; 
  4.  while (true) { 
  5.  list.add(String.valueOf(i).intern()); 
  6.  } 

输出异常结果:

  1. Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 
  2.  at java.util.Arrays.copyOf(Arrays.java:2245) 
  3.  at java.util.Arrays.copyOf(Arrays.java:2219) 
  4.  at java.util.ArrayList.grow(ArrayList.java:242) 
  5.  at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:216) 
  6.  at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:208) 
  7.  at java.util.ArrayList.add(ArrayList.java:440) 
  8.  at com.vprisk.knowledgeshare.MethodAreExample.main(MethodAreExample.java:15) 
  9.  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
  10.  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
  11.  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
  12.  at java.lang.reflect.Method.invoke(Method.java:606) 
  13.  at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) 

备注:网上的例子运行后会抛出java.lang.OutOfMemoryError:PermGen space异常。

2.2.3.1 关于String的intern()函数

intern()的作用:

如果当前的字符串在常量池中不存在,则放入到常量池中。

上面的代码不断将字符串添加到常量池,最终肯定会导致内存不足,抛出方法区的OOM。解释一下,为什么必须将上面的代码在JDK1.6之前运行。我们前面提到JDK1.7后,把常量池放入到堆空间中,这导致intern()函数的功能不同,代码清单如下:

  1. public static void testInternMethod(){ 
  2.  String str1 =new StringBuilder("hua").append("chao").toString(); 
  3.  System.out.println(str1.intern()==str1); 
  4.  String str2=new StringBuilder("ja").append("va").toString(); 
  5.  System.out.println(str2.intern()==str2); 

在场景jdk6,输出结果:

  1. false , false 

在场景jdk7,输出结果:

  1. true , false 

为什么了?

原因是在JDK1.6中,intern()方法会把首次遇到的字符串实例复制到常量池中,返回的也是常量池中的字符串的引用,而StringBuilder创建的字符串实例是在堆上面,所以必然不是同一个引用,返回false。在JDK1.7中,intern方法不再复制实例,常量池中只保存首次出现的实例的引用,因此intern()返回的引用和由StringBuilder创建的字符串实例是同一个。为什么对str2比较返回的是false呢?这是因为,JVM中内部在加载类的时候,就已经有”java”这个字符串,不符合“首次出现”的原则,因此返回false。

2.2.4 方法区的作用

方法区存放的是类信息、常量、静态变量等,是各个线程共享区域

2.2.5 方法区的运用

通过过设置虚拟机的参数 -XXpermSize 以及 -XX:MaxPermSize 限制方法区大小

2.2.6 方法区的使用场景

2.3. 虚拟机栈(VM Stack)

2.3.1 虚拟机栈的概念

虚拟机栈描述的是Java方法执行的内存模型:

每个方法被执行的时候都会同时创建一个栈帧 (StackFrame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程

2.3.1.1 局部变量表

局部变量表存放了编译器克制的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(Object reference)和字节码指令地址(returnAddress类型)。

2.3.1.1 操作栈

(编辑:PHP编程网 - 黄冈站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

推荐文章
    热点阅读