JVM内存区域主要包括以下部分:程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区。
JVM内存管理也能够理解为–jvm运行时数据区,jvm内存主要管理的就是这片内存区域。
html
java的堆是一个运行时的数据区,用来存储数据的单元,存放经过new关键字新建的对象和数组,对象从中分配内存。
在堆中声明的对象,是不能直接访问的,必须经过在栈中声明的指向该引用的变量来调用。引用变量就至关因而为数组或对象起的一个名称,之后就能够在程序中使用栈中的引用变量来访问堆中的数组或对象。 java
声明的对象是在堆内存中初始化的, 真正用来存储数据的,不能直接访问。
引用类型变量是保存在栈当中的,一个用来引用堆中对象的符号而已(指针)。linux
jvm堆的特色:程序员
各线程共享区域,存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。
类信息有:每一个类的全限定名、每一个类的直接超类的全限定名、该类是类仍是接口、该类型的访问修饰符、直接超接口的全限定名的有序列表。
每一个已装载类的详细信息:运行时常量池、字段信息、方法信息、静态变量、到类classloader的引用、到类class的引用。 web
java 常量池实际上分为两种形态:静态常量池 和运行时常量池 。算法
运行时常量池(Constant Pool Table),用于存放编译期生成的各类字面量、符号引用,String字符串、final变量值、类和结构的彻底限定名,方法的名称和描述符,字段的名称和描述符,这部份内容将在类加载后存放到方法区的运行时常量池中。它们以数组形式经过索引被访问,是外部调用与类联系及类型对象化的桥梁.
数组
在运行时,JVM从常量池中得到符号引用,而后在运行时解析成引用项的实际地址,最后经过常量池中的全限定名、方法和字段描述符,把当前类或接口中的代码与其它类或接口中的代码联系起来。
运行时常量池中的常量,基原本源于各个class文件中的常量池。
程序运行时,除非手动向常量池中添加常量(好比调用intern方法),不然jvm不会自动添加常量到常量池。 安全
运行时常量池除了存放编译期产生的Class文件的常量外,还可存放在程序运行期间生成的新常量,比较常见增长新常量方法有String类的internd()方法。String.intern()是一个Native方法,它的做用是:若是运行时常量池中已经包含一个等于此String对象内容的字符串,则返回常量池中该字符串的引用;若是没有,则在常量池中建立与此String内容相同的字符串,并返回常量池中建立的字符串的引用。不过JDK7的intern()方法的实现有所不一样,当常量池中没有该字符串时,再也不是在常量池中建立与此String内容相同的字符串,而改成在常量池中记录堆中首次出现的该字符串的引用,并返回该引用。 数据结构
因为运行时常量池在方法区中,咱们能够经过jvm参数:-XX:PermSize、-XX:MaxPermSize来设置方法区大小,从而间接限制常量池大小。
在jdk8中,移除了方法区,转而用Metaspace区域替代,因此咱们须要使用新的jvm参数:-XX:MaxMetaspaceSize多线程
可是,JDK1.7以前运行时常量池是方法区的一部分,JDK1.7及以后版本已经将运行时常量池从方法区中移了出来,在堆(Heap)中开辟了一块区域存放运行时常量池。
绝大部分 Java 程序员应该都见过 “java.lang.OutOfMemoryError: PermGen space “这个异常。这里的 “PermGen space”其实指的就是方法区。不过方法区和“PermGen space”又有着本质的区别。前者是 JVM 的规范,然后者则是 JVM 规范的一种实现,而且只有 HotSpot 才有 “PermGen space”,而对于其余类型的虚拟机,如 JRockit(Oracle)、J9(IBM) 并无“PermGen space”。因为方法区主要存储类的相关信息,因此对于动态生成类的状况比较容易出现永久代的内存溢出。
其实,移除永久代的工做从JDK1.7就开始了。JDK1.7中,存储在永久代的部分数据就已经转移到了Java Heap或者是 Native Heap。但永久代仍存在于JDK1.7中,并没彻底移除,譬如符号引用(Symbols)转移到了native heap;字面量(interned strings)转移到了java heap;类的静态变量(class statics)转移到了java heap。
元空间的好处总结:
- 字符串存在永久代中,容易出现性能问题和内存溢出。
- 类及方法的信息等比较难肯定其大小,所以对于永久代的大小指定比较困难,过小容易出现永久代溢出,太大则容易致使老年代溢出。
- 永久代会为 GC 带来没必要要的复杂度,而且回收效率偏低。
每一个方法在执行的同时都会建立一个栈帧用于存储局部变量,操做数栈,动态连接,方法出口等信息,每个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程
若是线程请求的栈深度大于虚拟机所容许的深度,将会抛出stackoverflowError;若是虚拟机能够动态扩展,可是没法申请到足够的内存时,就会抛出outOfMemoryError异常。
一、经过命令 ulimit -s 查看linux的默认栈空间大小,默认状况下 为10240 即10M
二、经过命令 ulimit -s 设置大小值 临时改变栈空间大小:ulimit -s 102400, 即修改成100M
三、能够在/etc/rc.local 内 加入 ulimit -s 102400 则能够开机就设置栈空间大小
四、在/etc/security/limits.conf 中也能够改变栈空间大小
soft stack 102400
虚拟机栈为虚拟机执行java方法提供服务,而本地方法栈为虚拟机使用到的本地方法服务。
这里描述的是C,C++里面的堆和栈。
从数据结构层次理解,栈是一种先进后出的线性表,只要符合先进后出的原则的线性表都是栈,至于采用的存储方式(实现方式)是顺序存储(顺序栈)仍是链式存储(链式栈)是没有关系的。堆则是二叉树的一种,有最大堆最小堆,排序算法中有经常使用的堆排序。
从系统层次理解,栈是系统为运行的程序分配的先进后出的存储区域。在执行函数时,函数内部局部变量的存储单元能够在栈上建立(针对CISC架构而 言,RISC架构下,局部变量的存储单元是在寄存器上建立),函数执行结束时这些存储单元自动被释放。堆是系统管理的能够被程序利用的全局存储空间,动态 内存分配就是从堆上分配。
如今计算机(串行执行机制),都直接在代码层次支持栈这种数据结构。这体如今,有专门的寄存器指向栈所在的地址,有专门的机器指令完成数据入栈出栈的操做。好比ARM指令中的stmfd和ldmfd。由于栈内存分配运算内置于处理器的指令集中,因此效率很高,可是支持的数据有限,通常是整数、指 针、浮点数等系统直接支持的数据类型,并不直接支持其余的数据结构。
和栈不一样,堆的数据结构并非由系统(不管是机器系统仍是操做系统)支持的,而是由函数库提供的。
看了堆和栈的介绍,堆和栈都是内存,就比如一张桌子里面有两个抽屉,一个抽屉里面存放一种东西,另一个抽屉存放另外一种东西,这样方便存取方便,找东西也比较快捷方便。
注意:每一个线程都有本身的栈空间哦;因此多线程的时候栈是不用担忧的,栈是线程私有的,不存在线程安全问题。
出栈后自动释放空间,因此这也就是后面要将的,垃圾收集历来没有说过要针对栈的,都是针对堆操做的。(java里面方法区和堆在这里都是堆空间)
上面说的堆和栈的高低位是地址空间的高低位,就是楼层编号同样。这里讲的高低位则是字节序的高低位,是不同的哦。
在汇编指令中,刚开始的cpu是16位的,分为高8位,低8位,分别用AH,AL指令操做高,低位。AH直接指向高位地址,Al直接指向低8位地址。
后来CPU支持32位了,为了向下兼容,32位的高位用EAX表示,那么64位的CPU呢?64位的高位尚未相应的指令,因此64位的二进制字节序取值须要使用位运算,向右移位操做。高低位的区分只是为了存取方便和读取效率高。
http://blog.csdn.net/u011080472/article/details/51321769
《深刻理解java虚拟机》
堆和栈:http://www.cnblogs.com/SinSay/archive/2008/11/12/1332076.html
http://zy77612.iteye.com/blog/1152225