zoukankan      html  css  js  c++  java
  • Java内存区域

    运行时数据区域:
    程序计数器
    通过改变计数器的值来选取下一条字节码指令,为了线程切换后能恢复到正确的执行位置,每条线程都有一个独立的程序计数器,即“线程私有”如果线程执行的是Java方法,那么计数器记录的是正在执行的虚拟机字节码指令的地址,如果是native方法,则计数器的值为空,此区域没有OutOfMemoryError情况。
    Java虚拟机栈
    线程私有,描述Java方法执行的内存模型,每个方法都会创建栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等。。方法调用直至完成,对应栈帧入栈到出栈。局部变量表内存空间在编译期间完成分配,运行期间不会改变其大小。线程请求栈的深度大于所允许的深度,则抛出StackOverflowError异常;如果扩展时无法申请到足够的内存,就抛出OutOfMemoryError异常。
    设置本地方法栈大小-Xoss(其实无效,应使用后面的),栈容量只由-Xss设定。
    单个线程下,抛出的都是StackOverflowError。
    如果是建立过多线程导致的内存溢出,可以通过减少最大堆和减少栈容量(每个线程被分配到的栈大小)来换取更多的线程。
    本地方法栈
    执行Native方法,会有StackOverflowError和OutOfMemoryError异常。
    Java堆
    jvm中最大的一块,线程共享,唯一目的是存放对象实例。垃圾收集器的主要管理区域,当在堆中没有内存完成实例分配,且堆无法再扩展时,将会抛出OutOfMemoryError异常。异常信息为java.lang.OutOfMemoryError :  java heap space
    堆的最小值-Xms,最大值-Xmx
    方法区
    线程共享,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等。JDK1.7之前称为“永久代”,1.7及以后被“元空间”替代。当方法去无法满足内存分配需求,抛出OutOfMemoryError。在经常动态生成大量Class的应用中,要注意类的回收,否则可能导致方法区内存溢出。
    通过-XX:PermSize和-XX:MaxPermSize限制方法区大小。
    运行时常量池
    存放编译期生成的各种字面量和符号引用,常量并非只有编译期能产生,在运行期间可能将新的常量放入常量池中,String的intern()方法便利用此特性。对于intern()的解释:1.如果常量池中存在该内容的String,则返回该串引用;2.如果不存在,则创建该String,返回引用。JDK1.7中常量池在方法区中,1.8后在堆中。
    再jdk1.6中,intern()方法会把首次遇到的字符串实例复制到永久代中,返回的也是永久带中该字符串实例的引用。
    在jdk1.7中,intern()方法不会再复制实例,只是在常量池中记录首次出现的实例引用。
    直接内存:
    OutOfMemoryError。
    DirectMemory容量可以通过-XX:MaxDirectMemorySize制定,如果不指定,则默认与Java堆最大值(-Xmx)一样。
    由DirectMemory导致的内存溢出,明显特征是Heap Dump文件中不会看见明显的异常。即如果OOM后Dump文件很小,程序直接或间接使用了NIO,则考虑是DirectMemory。
     
    jdk1.8内存图
    虚拟机中的对象
    对象的创建:new指令->检查常量池及是否加载初始化过->分配内存->保证线程安全->初始化为0->设置对象头->执行<init>方法
    对象所需的内存大小在类加载完成后便可完全确定。
    为对象分配内存的两种方式
    1. 指针碰撞:堆中内存绝对规整,用过的内存和空闲的内存放在分界点指针两边。 
    2. 空闲列表:堆中内存不规整,虚拟机维护一个列表,记录那些内存块可用,在分配时找到足够打的空间划分给实例。
    保证线程安全两种方式
    1. 对分配内存空间的动作进行同步处理,采用CAS配上失败重试的方式保证更新操作的原子性。
    2. 把内存分配的动作按照线程划分在不同的空间之中进行,每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲。
    对象的内存布局
      1.对象头
        1.运行时数据(Mark Word):如哈希码、GC分代年龄、锁状态标志、线程持有的锁等等,是非固定大小
        2.类型指针:对象指向它的类元数据的指针,通过该指针可以确定这个对象是哪个类的实例。
      2.实例数据:父类继承的和子类中定义的都在里面,相同宽度的字段总是分配到一起,在此前提下,父类定义变量出现在子类之前。
      3.对齐数据:不是必然存在,对象大小必须是8字节的整数倍,对象头部分正好是8的倍数,故当实例数据部分没有对齐时,需要通过对齐填充来补全。
    对象的访问定位
    通过句柄访问:在Java堆中划出一块内存作为句柄池,Java栈里面的变量指向句柄地址,句柄指向Java堆中的对象实例数据和方法区中的对象数据类型。优点:在对象被移动时只改变句柄的实例数据指针,不需要改变Java栈变量指针。
    通过直接指针访问:(jvm使用)Java栈里面的变量直接指向Java堆中的对象实例数据,对象实例数据中的指针指向方法区中的对象类型数据。优点:速度快,因为节省了一次指针定位的时间开销。
    类的静态变量存储在方法区,实例变量存储在堆中。
  • 相关阅读:
    最新的Zynq资料整理
    异步FIFO的FPGA实现
    Mac 下安装PHP遇到的问题
    php 实现推技术comet(转)
    高性能分布式内存队列系统beanstalkd(转)
    应对Memcached缓存失效,导致高并发查询DB的四种思路(l转)
    memcache 缓存失效问题(转)
    PHP.ini文件读取不到
    PHP5中魔术方法
    python mysql 单引号字符串过滤
  • 原文地址:https://www.cnblogs.com/qulingyuan/p/10877355.html
Copyright © 2011-2022 走看看