浅谈虚拟机内存区
副标题[/!--empirenews.page--]
1. Java 虚拟机内存区概述 我们在编写程序时,经常会遇到OOM(out of Memory)以及内存泄漏等问题。为了避免出现这些问题,我们首先必须对JVM的内存划分有个具体的认识。JVM将内存主要划分为:方法区、虚拟机栈、本地方法栈、堆、程序计数器。 2. Java 虚拟机运行时数据区 2.1. 运行时数据区划分 2.1.1 运行时数据区图 2.1.2 运行时数据区包括
2.2. 方法区(Method Area) 2.2.1 方法区的概念 方法区又叫静态区,存放的是已加载的类的基本信息、常量、静态变量等。它是各个线程共享区域。 比方说我们在写Java代码时,每个线程度可以访问同一个类的静态变量对象。由于使用反射机制的原因,虚拟机很难推测哪那个类信息不再使用,因此这块区域的回收很难。 2.2.1.1 静态块和非静态块有什么区别?
2.2.2 方法区的特点 线程间共享区域 2.2.3 方法区的异常 对这块区域主要是针对常量池回收,值得注意的是JDK1.7已经把常量池转移到堆里面了。同样,当方法区无法满足内存分配需求时,会抛出OutOfMemoryError。制造方法区内存溢出,注意,必须在JDK1.6及之前版本才会导致方法区溢出,原因后面解释,执行之前,可以把虚拟机的参数-XXpermSize和-XX:MaxPermSize限制方法区大小。 代码清单如下:
输出异常结果:
备注:网上的例子运行后会抛出java.lang.OutOfMemoryError:PermGen space异常。 2.2.3.1 关于String的intern()函数 intern()的作用: 如果当前的字符串在常量池中不存在,则放入到常量池中。 上面的代码不断将字符串添加到常量池,最终肯定会导致内存不足,抛出方法区的OOM。解释一下,为什么必须将上面的代码在JDK1.6之前运行。我们前面提到JDK1.7后,把常量池放入到堆空间中,这导致intern()函数的功能不同,代码清单如下:
在场景jdk6,输出结果:
在场景jdk7,输出结果:
为什么了? 原因是在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 操作栈 (编辑:晋中站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |