zoukankan      html  css  js  c++  java
  • JVM 09.1 运行时数据区 堆 核心概述

    版权声明:源出处:尚硅谷JVM

    博客来源于大佬整理

    一个进程对应一个jvm实例,一个运行时数据区,又包含多个线程,这些线程共享了方法区和堆,每个线程包含了程序计数器、本地方法栈和虚拟机栈。

    核心概述

    1.一个jvm实例只存在一个堆内存,堆也是java内存管理的核心区域

    2.Java堆区在JVM启动的时候即被创建,其空间大小也就确定了。是JVM管理的最大一块内存空间(堆内存的大小是可以调节的)

    3.《Java虚拟机规范》规定,堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的。

    4.所有的线程共享java堆,在这里还可以划分线程私有的缓冲区(TLAB:Thread Local Allocation Buffer).(面试问题:堆空间一定是所有线程共享的么?不是,TLAB线程在堆中独有的)

    5.《Java虚拟机规范》中对java堆的描述是:所有的对象实例以及数组都应当在运行时分配在堆上。从实际使用的角度看,“几乎”所有的对象的实例都在这里分配内存 (‘几乎’是因为可能存储在栈上,另见逃逸分析)

    6。数组或对象永远不会存储在栈上,因为栈帧中保存引用,这个引用指向对象或者数组在堆中的位置。

    7.在方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才会被移除

    8.堆,是GC(Garbage Collection,垃圾收集器)执行垃圾回收的重点区域

    配置堆内存及查看jvm进程

    编写HeapDemo/HeapDemo1代码:

    复制代码
    public class HeapDemo {
        public static void main(String[] args) {
            System.out.println("start...");
            try {
                Thread.sleep(1000000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("end...");
        }
    }
    复制代码

    首先对虚拟机进行配置,如图 Run-Edit configurations:

    在jdk目录,jdk1.8.0_171.jdk/Contents/Home/bin下找到jvisualvm 运行(或者直接终端运行jvisualvm),查看进程,可以看到我们设置的配置信息:

     可以看到HeapDemo配置-Xms10m, 分配的10m被分配给了新生代3m和老年代7m:

     分析SimpleHeap的jvm情况

    复制代码
    public class SimpleHeap {
        private int id;//属性、成员变量
    
        public SimpleHeap(int id) {
            this.id = id;
        }
    
        public void show() {
            System.out.println("My ID is " + id);
        }
        public static void main(String[] args) {
            SimpleHeap sl = new SimpleHeap(1);
            SimpleHeap s2 = new SimpleHeap(2);
    
            int[] arr = new int[10];
    
            Object[] arr1 = new Object[10];
        }
    }
    复制代码

    堆的细分内存结构

    JDK 7以前: 逻辑上分为新生区+养老区+永久区(即Xms/Xmx分配的内存物理上没有涉及永久区)

    • Young Generation Space:又被分为Eden区和Survior区
    • Tenure generation Space: ==Old/Tenure==
    • Permanent Space: ==Perm==

    JDK 8以后: 逻辑上分为新生区+养老区+元空间(即Xms/Xmx分配的内存物理上没有涉及元空间)
    • Young Generation Space:又被分为Eden区和Survior区 
    • Tenure generation Space: ==Old/Tenure==
    • Meta Space: ==Meta==

    设置堆内存大小与OOM

    1.Java堆区用于存储java对象实例,堆的大小在jvm启动时就已经设定好了,可以通过 "-Xmx"和 "-Xms"来进行设置

    • -Xms 用来设置堆空间(年轻代+老年代)的初始内存大小,等价于 -XX:InitialHeapSize
      • -X 是jvm的运行参数
      • ms 是memory start
    • -Xmx 用于设置堆的最大内存,等价于 -XX:MaxHeapSize

    2.一旦堆区中的内存大小超过 -Xmx所指定的最大内存时,将会抛出OOM异常。

    • 默认情况下,初始内存大小:物理内存大小/64;最大内存大小:物理内存大小/4。
    • 手动设置:-Xms600m -Xmx600m

    3.通常会将-Xms和-Xmx两个参数配置相同的值,其目的就是为了能够在java垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小,从而提高性能。

    • 比如说:默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制。
      因此服务器一般设置-Xms、-Xmx相等以避免在每次GC 后调整堆的大小

    4.查看设置的堆内存参数:

    • 方式一: ==终端输入jps== , 然后 ==jstat -gc 进程id==
    • 方式二:(控制台打印)Edit Configurations->VM Options 添加 ==-XX:+PrintGCDetails==

    查看堆内存大小测试代码

    复制代码
    public class HeapSpaceInitial {
        public static void main(String[] args) {
    
            //返回Java虚拟机中的堆内存总量
            long initialMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;
            //返回Java虚拟机试图使用的最大堆内存量
            long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;
    
            System.out.println("-Xms : " + initialMemory + "M");//-Xms : 245M
            System.out.println("-Xmx : " + maxMemory + "M");//-Xmx : 3641M
    
            System.out.println("系统内存大小为:" + initialMemory * 64.0 / 1024 + "G");//系统内存大小为:15.3125G
            System.out.println("系统内存大小为:" + maxMemory * 4.0 / 1024 + "G");//系统内存大小为:14.22265625G
    
            try {
                Thread.sleep(1000000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    复制代码

    堆大小分析

    设置堆大小为600m,打印出的结果为575m,这是因为幸存者区S0和S1各占据了25m,但是他们始终有一个是空的,存放对象的是伊甸园区和一个幸存者区。

     

    OOM示例

    java.lang.OutOfMemoryError: Java heap space。代码示例:

    复制代码
    /**
     * -Xms600m -Xmx600m
     */
    public class OOMTest {
        public static void main(String[] args) {
            ArrayList<Picture> list = new ArrayList<>();
            while(true){
                try {
                    Thread.sleep(20);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                list.add(new Picture(new Random().nextInt(1024 * 1024)));
            }
        }
    }
    
    class Picture{
        private byte[] pixels;
    
        public Picture(int length) {
            this.pixels = new byte[length];
        }
    }
    复制代码
  • 相关阅读:
    MYSQL 神奇的操作insert into test select * from test;
    mysql排序字段为空的排在最后面
    Redis有效时间设置及时间过期处理
    Dom4j 使用简介
    ASP.NET中使用多个runat=server form(转)
    谨以此文献给才毕业25年的朋友(转)
    门户网站
    庄思浩和BEA公司
    是什么限制了我们面向对象(的开发) (转)
    模态窗口和非模态窗口
  • 原文地址:https://www.cnblogs.com/superxuezhazha/p/13329064.html
Copyright © 2011-2022 走看看