精华内容
下载资源
问答
  • JVM内存结构

    2021-03-03 17:12:35
    JVM内存结构

    JVM内存结构

    JVM内存结构思维导图

    展开全文
  • JVM01_JVM内存结构

    万次阅读 2020-08-20 15:11:00
    JVM内存结构 1. 程序计数器 Program Counter Register 作用: 记住 当前线程 的下一条 JVM字节码指令 的 执行地址 ,便于进行 线程切换 特点: 是 线程私有 的,保证了各线程不会互相影响 不会存在内存溢出 为...

    JVM内存结构

    在这里插入图片描述


    1. 程序计数器 Program Counter Register

    作用: 记住 当前线程 的下一条 JVM字节码指令执行地址 ,便于进行 线程切换

    特点:

    1. 线程私有 的,保证了各线程不会互相影响
    2. 不会存在内存溢出
    为什么要使用PC寄存器记录当前线程的执行地址呢?
    答:因为CPU需要不停的切换各个线程,这时候切换回来以后,就得知道接着从哪开始继续执行。
    
    PC寄存器为什么会被设定为线程私有?
    答:多线程在一个特定的时间段内只会执行其中某一个线程方法,CPU会不停的做任务切换,这样必然会导致经常中断或恢复。
    为了能够准确的记录各个线程正在执行的当前字节码指令地址,所以为每个线程都分配了一个PC寄存器,
    让每个线程都独立计算,不会互相影响。
    

    2. Java虚拟机栈 JVM Stacks

    虚拟机栈是 线程运行 需要的内存空间

    一个栈由多个 栈帧 组成,栈帧对应 方法调用 时占用的内存空间,每个线程只能有一个 活动栈帧

    栈帧用于存放基本数据类型、对象的引用、方法出口等,是 线程私有

    问题1:垃圾回收是否涉及栈内存?
    答:不涉及,栈内存是JVM自动管理的,方法调用时入栈,方法运行结束后栈帧出栈,内存自动释放。
    
    问题2:栈内存分配越大越好吗?
    答:不是,因为物理内存的大小是一定的,所以栈内存越大,线程数就越小。
    
    问题3:方法内的局部变量是否是线程安全的?
    如果方法内的局部变量没有逃离方法的作用范围,它就是线程安全的。
    如果局部变量引用了对象并逃离了方法的作用范围,它就有线程安全问题。
    

    栈内存溢出:StackOverflowError

    方法过多导致栈内存溢出: 无递归边界的递归调用
    栈帧过大导致栈内存溢出

    分配栈内存的大小:

    -Xss1m
    

    线程运行诊断:

    案例1:cpu占用过高

    步骤1:top命令查看进程的cpu占用情况,锁定进程id
    
    步骤2:用如下命令进一步定位占用出问题的线程id:
    ps H -eo pid,tid,%cpu | grep 进程id
    
    步骤3:将10进制的线程id转成16进制,比如32665 ==> 7F99
    
    步骤4:使用jstack命令查看进程信息,根据线程id 7F99查找线程,可看到线程的详细信息,进而定位出问题代码的行数
    jstack 进程id
    

    3. 本地方法栈 Native Method Stacks

    本地方法栈 是JVM给 本地方法 的调用提供内存空间的栈

    本地方法 是由其他语言(C、C++ 、汇编语言)编写的与操作系统底层交互的api


    4. 堆 Heap

    通过 new 关键字创建的对象都会使用堆内存

    堆是 线程共享 的,堆中对象需要 考虑线程安全问题

    垃圾回收机制 ,堆中不再被引用的对象将被回收释放

    堆内存溢出:java.lang.OutOfMemoryError:java heap space

    public static void main(String[] args) {
    	int i = 0;
    	ArrayList<String> list = new ArrayList<>();
    	String a = "BLU";
    	try {
    		while(true) {
    			list.add(a);
    			a = a+a;
    			i++;
    		}
    	} catch (Throwable e) {
    		e.printStackTrace();
    		System.out.println(i);
    	}
    	
    }
    
    java.lang.OutOfMemoryError: Java heap space
    26
    

    分配栈空间的大小:

    -Xmx4g
    

    堆内存诊断:

    jps工具查看当前系统有哪些java进程
    jmap工具查看堆内存的占用情况
    jconsole工具:是图形界面的多功能的监测工具
    

    示例代码:

    public static void main(String[] args) throws InterruptedException {
    	System.out.println("1.....");
    	Thread.sleep(30000);
    	byte[] array = new byte[1024*1024*10];
    	System.out.println("2.....");
    	Thread.sleep(30000);
    	array = null;
    	System.gc();
    	System.out.println("3.....");
    	Thread.sleep(400000);
    }
    

    使用jmap工具测试实例:

    运行开始,控制台打印1......
    
    jps命令查看进程id:
    C:\Users\73691>jps
    12080 demo02
    
    jmap -heap 12080查看堆内存占用情况(堆内存使用1.68MB):
    Heap Usage:
    PS Young Generation
    Eden Space:
       capacity = 29360128 (28.0MB)
       used     = 1762032 (1.6804046630859375MB)
       free     = 27598096 (26.319595336914062MB)
       6.001445225306919% used
    
    30s后,控制台打印2......,此时byte数组对象创建完毕
    jmap -heap 12080查看堆内存占用情况(堆内存使用11.68MB):
    Heap Usage:
    PS Young Generation
    Eden Space:
       capacity = 29360128 (28.0MB)
       used     = 12247808 (11.680419921875MB)
       free     = 17112320 (16.319580078125MB)
       41.715785435267854% used
    
    30s后,控制台打印3......,此时byte数组对象被垃圾回收
    jmap -heap 12080查看堆内存占用情况(堆内存使用0.56MB):
    Heap Usage:
    PS Young Generation
    Eden Space:
       capacity = 29360128 (28.0MB)
       used     = 587224 (0.5600204467773438MB)
       free     = 28772904 (27.439979553222656MB)
       2.000073024204799% used
    

    使用jconsole工具测试示例:

    运行示例代码,使用jconsole命令打开监测工具,连接进程后即可实时查看堆内存使用情况:

    在这里插入图片描述
    使用jvisualvm监测:

    示例代码:

    public class demo03 {
    	public static void main(String[] args) throws InterruptedException {
    		List<Student> list = new ArrayList<Student>();
    		for (int i = 0; i < 200; i++) {
    			list.add(new Student());
    		}
    		Thread.sleep(100000000);
    	}
    }
    
    class Student {
    	private byte[] big = new byte[1024*1024];
    }
    

    执行,使用jconsole工具查看堆内存使用量,点击执行GC:

    在这里插入图片描述
    堆内存占用依然很高:

    在这里插入图片描述
    使用 jmap 查看详细信息(老年代Old Generation占用202.11MB):

    Heap Usage:
    PS Young Generation
    Eden Space:
       capacity = 106430464 (101.5MB)
       used     = 7077904 (6.7500152587890625MB)
       free     = 99352560 (94.74998474121094MB)
       6.650261338708436% used
    From Space:
       capacity = 4718592 (4.5MB)
       used     = 0 (0.0MB)
       free     = 4718592 (4.5MB)
       0.0% used
    To Space:
       capacity = 4718592 (4.5MB)
       used     = 0 (0.0MB)
       free     = 4718592 (4.5MB)
       0.0% used
    PS Old Generation
       capacity = 304087040 (290.0MB)
       used     = 211930720 (202.11288452148438MB)
       free     = 92156320 (87.88711547851562MB)
       69.69409811085669% used
    

    使用jvisualvm工具,点击堆 Dump(堆转储)

    在这里插入图片描述
    点击查找 20 保留大小最大的对象,可以查看占用堆内存较大的对象信息:

    在这里插入图片描述


    5. 方法区 method Area

    方法区是线程共享的区,存储了类结构相关信息:类的成员变量、方法数据、方法和构造器代码,还有一个运行时常量池

    方法区在虚拟机启动时被创建,方法区逻辑上是堆的组成部分

    方法区也会内存溢出

    在这里插入图片描述
    在这里插入图片描述
    串池:StringTable

    作用:避免字符串重复创建,提升性能,减少内存的开销

    https://blog.csdn.net/soonfly/article/details/70147205

    String s1 = "a";
    String s2 = "b";
    String s3 = "a"+"b";
    String s4 = s1 + s2;	
    String s5 = "ab";
    String s6 = s4.intern();
    
    问:
    System.out.println(s3==s4);
    System.out.println(s3==s5);
    System.out.println(s3==s6);
    
    String s1 = "a"; 			串池中存入a
    String s2 = "b"; 			串池中存入b
    String s3 = "a"+"b"; 		常量的拼接,在编译期值已经确定是ab,串池中存入ab
    String s4 = s1 + s2;		相当于:new StringBuilder().append("a").append("b").toString();	new 的对象存在堆中
    所以System.out.println(s3==s4);false
    String s5 = "ab"; 			串池中已有ab,引用即可
    所以System.out.println(s3==s5);true
    String s6 = s4.intern();	尝试将字符串对象ab存入串池,并返回串池中的对象ab,所以s6引用的对象为串池中的ab
    所以System.out.println(s3==s6);true
    
    
    --------------------------------------------------------------------------------------------------------------------
    
    String x2 = new String("c")+ new String("d");
    String x1 = "cd";
    x2.intern();
    
    问:
    System.out.println(x1==x2);
    如果调换了最后两行代码的位置呢?
    如果是jdk1.6呢?
    

    StringTable的位置:在1.6中,StringTable是JVM常量池的一部分,在永久代中,1.7后,StringTable转移至堆中
    原因:永久代在 Full GC 时才会触发垃圾回收,触发事件晚,内存回收效率不高。而堆只要 Minor GC 就会触发垃圾回收


    6. 堆外内存(直接内存)

    堆外内存定义: 内存对象分配在Java虚拟机的堆以外的内存,这些内存直接 受操作系统管理(而不是虚拟机),这样做的结果就是能够在一定程度上减少垃圾回收对应用程序造成的影响。使用未公开的Unsafe和NIO包下ByteBuffer来创建堆外内存。

    在这里插入图片描述


    7. 垃圾回收

    如何判断对象可以回收:

    • 引用计数法(Lisp,Python,Ruby等语言使用的垃圾收集算法)

    引用计数法的原理:
    在对象头中分配一个空间来保存该对象被引用的次数。如果该对象被其它对象引用,则它的引用计数加一,如果删除对该对象的引用,那么它的引用计数就减一,当该对象的引用计数为0时,那么该对象就会被回收。

    引用计数法存在的问题:

    1. 需要额外的空间来存储计数器,以及繁琐的更新操作。
    2. 不能处理环形数据。如果有两个对象相互引用,那么这两个对象就不能被回收,因为它们的引用计数始终为1。这就是“内存泄漏”问题。

    在这里插入图片描述

    • 可达性分析算法(java语言使用的垃圾收集算法)

    可达性分析算法的原理:
    将一系列GC Roots作为初始的存活对象合集(live set),然后从该合集出发,探索所有能够被该合集引用到的对象,并将其加入到该和集中,这个过程称之为标记(mark)。 最终,未被探索到的对象便是死亡的,是可以回收的。

    展开全文
  • jvm内存结构

    2021-02-25 17:06:33
    jvm内存结构 jvm内存结构包含什么? Java堆(Heap) 方法区(Method Area) 程序计数器 JVM栈 本地方法栈 jvm内存结构详细介绍 方法区:主要是存储类信息,常量池(static常量和static变量),编译后的代码...

    jvm内存结构

    jvm内存结构包含什么?

    • Java堆(Heap)
    • 方法区(Method Area)
    • 程序计数器
    • JVM栈
    • 本地方法栈

    jvm内存结构详细介绍

    • 方法区:主要是存储类信息,常量池(static常量和static变量),编译后的代码(字节码)等数据
    • 堆:初始化的对象,成员变量 (那种非static的变量),所有的对象实例和数组都要在堆上分配
    • 栈:栈的结构是栈帧组成的,调用一个方法就压入一帧,帧上面存储局部变量表,操作数栈,方法出口等信息,局部变量表存放的是8大基础类型加上一个应用类型,所以还是一个指向地址的指针
    • 本地方法栈:主要为Native方法服务
    • 程序计数器:记录当前线程执行的行号
    展开全文
  • JVM 内存结构

    2021-03-04 21:04:23
    第一章 JVM相关知识点 第一节 JVM内存结构

    第一章 JVM相关知识点

    第一节 JVM内存结构

    JVM内存模型可以分为两个部分

    所有线程共有:堆和方法区
    线程私有: 虚拟机栈,本地方法栈和程序计数器

    在这里插入图片描述

    1. 堆(Heap)

    堆内存是所有线程共有的,可以分为两个部分:新生代和老年代。
    堆是java虚拟机所管理的内存中最大的一块内存区域,该内存区域存放了对象实例及数组(但不是所有的对象实例都在堆中)。

    在这里插入图片描述
    堆内存 = 新生代 + 老生代(默认大小比例1:2)。
    新生代 = Eden + Survivor0 + Survivor1组成(默认比例8:1:1)

    2. 方法区(Method Area)

    方法区是所有线程共有的
    用于存储虚拟机加载的类信息、常量、静态变量、是各个线程共享的内存区域。

    3. 虚拟机栈(JVM Stack)

    声明周期与线程相同,是线程私有的。
    描述的是java方法执行的内存模型:每个方法被执行的时候都会创建一个”栈帧”,用于存储局部变量表(包括参数)、操作栈、方法出口等信息。

    每个方法被调用到执行完的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
    栈帧由三部分组成:局部变量区、操作数栈、帧数据区。

    4. 本地方法栈(Native Stack)

    线程私有
    与虚拟机栈基本类似,区别在于虚拟机栈为虚拟机执行的java方法服务,而本地方法栈则是为Native方法服务。(栈的空间大小远远小于堆)

    5. 程序计数器(PC Register)

    线程私有,是最小的一块内存区域
    它的作用是当前线程所执行的字节码的行号指示器,在虚拟机的模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、异常处理、线程恢复等基础功能都需要依赖计数器完成。

    展开全文
  • jvm 内存结构

    2019-11-08 23:19:59
    jvm内存结构 文章目录jvm内存结构运行时数据区虚拟机栈本地方法栈堆区方法区程序计数器执行引擎本地库接口类加载器系统 jvm类加载器系统运行时数据区本地库接口执行引擎虚拟机栈本地方法栈堆区方法区程序计数器 运行...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 12,225
精华内容 4,890
关键字:

jvm内存结构