1、JVM中的内存结构

从OS的角度来看,JVM运行时会把一部分内存虚拟机化,所以把内存分为直接内存(未被虚拟机化的内存)和运行时数据区(被虚拟机化的内存)

JVM的运行时数据区若从线程的角度来看,可分为线程共享的区域和线程私有的区域

线程共享区主要由方法区、堆组成,其中方法区还会划分出一部分作为运行时常量池

线程私有去区主是由一条条线程的私有内存组成的,每一条线程的私有区域是由虚拟机栈、本地方法栈以及程序计数器来构成的

 2、运行时数据区中的线程私有区域

(1)虚拟机栈

一条线程可以执行多个方法,每一个方法在执行时所需要的数据、指令 、返回地址都会存储在虚拟机栈中,每一个方法都会打包成一个栈帧

虚拟机栈的大小缺省为 1M,可用参数 –Xss 调整大小,例如-Xss256k

每一个栈帧都是由局部变量表、操作数据栈、动态连接以及完成出口组成

当线程执行的方法中有调用别的方法时,会把每一个方法包装到一个栈帧中,将栈帧压到虚拟机栈中,后入栈的方法先出栈执行,从而实现方法中的调用逻辑

  1、栈帧中的局部变量表:长度为32位,用来存放方法中的变量,包括Java中的8大基础数据类型,局部对象的引用

  2、栈帧中的操作数据栈:存放 java 方法执行的操作数的,它就是一个栈,先进后出的栈结构,操作数栈,就是用来操作的,操作的的元素可以是任意的 java 数据类型,所

以我们知道一个方法刚刚开始的时候,这个方法的操作数栈就是空的。

  3、栈帧中的动态连接:一个方法调用了另外的其他方法时,就是通过常量池中指向方法的符号引用来表示的,动态连接会将这些符号引用转化为调用方法的直接引用

4、栈帧中的完成出口:正常返回(调用程序计数器中的地址作为返回)、异常的话(通过异常处理器表<非栈帧中的>来确定)

   方法执行完成步骤:1)恢复局部变量表和操作数栈

          2)把返回值压入调用方法的栈帧的操作数栈中

          3)调用程序计数器指向方法调用的下一条指令

异常的话:(通过异常处理表<非栈帧中的>来确定)

5、当要执行的方法过多或方法执行的所需要的资源过大时,会造成栈的溢出(OOM)

   HotSpot 版本中栈的大小是固定的,是不支持拓展的

(2)本地方法栈

本地方法栈的功能与虚拟机栈的功能类似,虚拟机栈用来管理程序中函数的调用,而本地方法栈服务于native方法,用来调用C语言编写的本地方法

(3)程序计数器  

占据较小的内存空间,主要用来记录各个线程执行的字节码的地址,作为当前线程执行的字节码的行号指示器;各线程之间独立存储,互不影响。

程序技术器也是JVM中唯一不会发生OOM(内存溢出)的内存区域

3、运行时数据区中的线程共享区域

(1)方法区

位于线程共享区,其中存储了的每一个类的结构信息(运行时常量池、字段、方法数据、构造函数、普通方法的字节码内容等等)

JDK1.7中,通常会将方法区称为永久代,永久代的静态变量和运行时常量池转移到了堆中,其余部分则存储在 JVM 的非堆内存中

JDK1.8之后,用元空间替换了永久代,存储位置是本地内存

不管常量池存储在哪,逻辑上属于方法区

Java8 为什么使用元空间替代永久代?

移除永久代是为了融合 HotSpot JVM 与 JRockit VM 而做出的努力,因为 JRockit 没有永久代,所以不需要配置永久代。

永久代内存经常不够用或发生内存溢出,抛出异常 java.lang.OutOfMemoryError: PermGen。这是因为在 JDK1.7 版本中,指定的 PermGen 区大小为8M,由于 PermGen 中类的元数据信息在每次 FullGC 的时候都可能被收集,回收率都偏低,成绩很难令人满意;还有为 PermGen 分配多大的空间很难确定,PermSize 的大小依赖于很多因素,比如,JVM 加载的 class 总数、常量池的大小和方法的大小等。

运行时常量池
运行时常量池(Runtime Constant Pool)是每一个类或接口的常量池(Constant_Pool)的运行时表示形式
它包括了若干种不同的常量:从编译期可知的数值字面量到必须运行期解析后才能获得的方法或字段引用
运行时常量池是方法区的一部分。运行时常量池相对于 Class 常量池的另外一个重要特征是具备动态性
(2)堆
堆是JVM中划分的最大的一块内存区域,我们平时创建的对象基本上都在堆中存储的
堆空间一般在程序启动的时候就会分配,但是不一定会用到,随着创建的对象越来越多,占据的内存越来越大,可利用的空间越来越小,JVM会对不使用的对象进行回收,这就是GC,JVM的GC就是对堆这块内存区域进行垃圾回收
 
什么样的对象不会存储在堆中?
平时我们常用的对象可以分为两种,一种时普通对象,一种是基本数据类型(Int、Short、Lang、Boolean、Double、Float、Byte、Char)的对象
普通对象创建时,JVM都会在堆上创建对象,其他地方使用的是这个对象的引用
基本数据类型的对象创建时,如果是在方法中创建的,那么对象会被存储到栈中的操作数据栈上,其他场景,都是存储到堆中的
设置堆大小参数:
-Xms:堆的最小值
-Xmx:堆的最大值
-Xmn:新生代的大小
-XX:NewSize;新生代最小值
-XX:MaxNewSize:新生代最大值
JVM中堆的划分
JVM中将堆划分为老年代和新生代,新生代进一步被划分为Eden区和Survivor区,Survivor区又被进一步划分为Form和To两部分
 
所有的对象创建时会优先分配在新生代中的Eden,若对象占据较大内存时会分配到老年代中,这是为了避免Eden区内存用尽提前发生GC,因为一般的JVM堆内存的划分中老年代都大于新生代
当Eden区中的对象年龄达到15岁时,也就是说经过了15次GC后,会进入到老年代
 
为什么要经过15次GC呢?
因为对象中记录当前对象的GC年龄的属性只有4位,最高能记录2^4-1次,所以经过15次GC之后对象会由新生代进入老年代
 
4、简单说一下常遇到的内存溢出的原因
(1)栈溢出
  HotSpot 版本中栈的大小是固定的,是不支持拓展的

  栈区的空间 JVM 没有办法去限制的,因为 JVM 在运行过程中会有线程不断的运行,没办法限制,所以只限制单个虚拟机栈的大小
  方法执行时是把一个个方法包装成一个个栈帧的,所以当出现方法的无限调用的时候,容易出现栈溢出现象
(2)堆溢出
  创建对象时申请的空间超出了堆的最大空间,会出现对溢出的现象
(3)方法区溢出
  方法区溢出分为两种,一种时常量池中内存不足造成的溢出,一种时方法区中class类造成的溢出
  对象可以通过GC来进行回收,但Class类的回收条件十分苛刻,不易回收
  
  Class类回收的条件有什么?
  1)堆中不存在任何这个类的实例
  2)加载这个类的ClassLoader已经被回收
  3)这个类没有任何地方被引用
  4)这个类没有任何地方通过反射来调用
 

  

2、JVM的内存的更多相关文章

  1. jvm的内存分配总结

    最近看了周志明版本的<深入理解Java虚拟机>第一版和第二版,写的很好,收获很多,此处总结一下.   jvm中内存划分:   如上图,一共分为五块,其中: 线程共享区域为: 1.java堆 ...

  2. java语言:Linux与JVM的内存关系分

    在一些物理内存为8g的服务器上,主要运行一个Java服务,系统内存分配如下:Java服务的JVM堆大小设置为6g,一个监控进程占用大约 600m,Linux自身使用大约800m.从表面上,物理内存应该 ...

  3. 【转】JVM 堆内存设置原理

    堆内存设置 原理 JVM堆内存分为2块:Permanent Space 和 Heap Space. Permanent 即 持久代(Permanent Generation),主要存放的是Java类定 ...

  4. JVM的内存区域划分

            JVM的内存区域划分 学过C语言的朋友都知道C编译器在划分内存区域的时候经常将管理的区域划分为数据段和代码段,数据段包括堆.栈以及静态数据区.那么在Java语言当中,内存又是如何划分的 ...

  5. 转: 关于Linux与JVM的内存关系分析

    转自: http://tech.meituan.com/linux-jvm-memory.html Linux与JVM的内存关系分析 葛吒2014-08-29 10:00 引言 在一些物理内存为8g的 ...

  6. 设置TOMCAT的JVM虚拟机内存大小

    你知道如何设置TOMCAT的JVM虚拟机内存大小吗,这里和大家分享一下,JAVA程序启动时JVM都会分配一个初始内存和最大内存给这个应用程序.这个初始内存和最大内存在一定程度都会影响程序的性能. 设置 ...

  7. 解决JVM最大内存设置问题

    这里和大家讨论一下如何获得JVM最大内存,在命令行下用java-XmxXXXXM-version命令来进行测试,然后逐渐的增大XXXX的值,如果执行正常就表示指定的内存大小可用,否则会打印错误信息. ...

  8. 图解JVM在内存中申请对象及垃圾回收流程

    http://longdick.iteye.com/blog/468368 先看一下JVM的内存模型: 从大的方面来讲,JVM的内存模型分为两大块: 永久区内存( Permanent space )和 ...

  9. Linux与JVM的内存关系分析

    引言 在一些物理内存为8g的server上,主要执行一个Java服务,系统内存分配例如以下:Java服务的JVM堆大小设置为6g,一个监控进程占用大约600m,Linux自身使用大约800m. 从表面 ...

  10. jvm的内存管理【转】

    [转]JVM内存管理 这些日子一直在研究jvm内存管理的东西,网上的知识很多,总结一下,能沉淀下来的就是自己的! 首先,刚学java的时候就知道java类文件是以 .java为后缀的文件,经过java ...

随机推荐

  1. 用java中的Arraylist实现电话本系统管理

    大致思路:创建一个电话本条目的类,在主类中实例化.用实例化的对象调用构造参数接收输入值,然后将此对象存入Arraylist的对象中,实现动态添加电话本条目. 该系统具备添加.删除.修改.查询所有和按姓 ...

  2. MySQL锁这块石头似乎没有我想的那么重

    前言 前言为本人写这篇文章的牢骚,建议跳过不看.   之前好几次都想好好的学习MySQL中的锁,但是找了几篇文章,看了一些锁的类型有那么多种,一时间也没看懂是什么意思,于是跟自己说先放松下自己,便从书 ...

  3. 总结java中文件拷贝剪切的5种方式-JAVA IO基础总结第五篇

    本文是Java IO总结系列篇的第5篇,前篇的访问地址如下: 总结java中创建并写文件的5种方式-JAVA IO基础总结第一篇 总结java从文件中读取数据的6种方法-JAVA IO基础总结第二篇 ...

  4. MySQL常用指令,java,php程序员,数据库工程师必备。程序员小冰常用资料整理

    MySQL常用指令,java,php程序员,数据库工程师必备.程序员小冰常用资料整理 MySQL常用指令(备查) 最常用的显示命令: 1.显示数据库列表. show databases; 2.显示库中 ...

  5. JAVA中 错误代码是 the public type must be defined in its own file 解决方法 android开发 java编程

    一般是由于定义的JAVA类同文件名不一致: 解决方法: 1.把文件名修改同XYZ一样的名字: 2.把类名修改成同文件名:

  6. Python的UI库

    https://github.com/realitix/vulkan https://github.com/swistakm/pyimgui https://wxpython.org

  7. 支付-微信h5

    背景 h5支付分两种 1.浏览器 2.app 浏览器里的h5,最终也会跳转到app. 而app里的h5,本质是公众号.在微信里叫公众号,支付宝叫服务窗. 这里主要讲微信h5. 核心原理 最终目标是下单 ...

  8. MyBatis源码流程分析

    mybatis核心流程三大阶段 Mybatis的初始化  建造者模式 建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象.这种类型的设计模式属于创建型模式,它提 ...

  9. 整理的网上的MySQL优化文章总结

    MySQL优化 Linux优化 IO优化 调整Linux默认的IO调度算法. IO调度器的总体目标是希望让磁头能够总是往一个方向移动,移动到底了再往反方向走,这恰恰就是现实生活中的电梯模型,所以IO调 ...

  10. python的运算符及数据类型的转换

    python 目录 python 1.算术运算符 2.比较运算符 3.赋值运算符和复合赋值运算符 4.位运算符 5.逻辑运算符 6.成员运算符 7.身份运算符 8.常见的数据类型转换 1.算术运算符 ...