首页 > 系统相关 >jvm-1.内存结构

jvm-1.内存结构

时间:2022-10-15 21:24:25浏览次数:75  
标签:ab 常量 线程 内存 jvm 结构 out String

内存结构

1. 程序计数器

1.1 定义

Program Counter Register 程序计数器(寄存器)

  • 作用,是记住下一条jvm指令的执行地址

  • 特点:

    • 是线程私有的

    • 不会存在内存溢出

1.2 作用

0 : getstatic #20 // PrintStream out = System.out;
3 : astore_1 // --
4 : aload_1 // out.println(1);
5 : iconst_1 // --
6 : invokevirtual #26 // --
9 : aload_1 // out.println(2);
10 : iconst_2 // --
11 : invokevirtual #26 // --
14 : aload_1 // out.println(3);
15 : iconst_3 // --
16 : invokevirtual #26 // --
19 : aload_1 // out.println(4);
20 : iconst_4 // --
21 : invokevirtual #26 // --
24 : aload_1 // out.println(5);
25 : iconst_5 // --
26 : invokevirtual #26 // --
29 : return

 

2. 虚拟机栈

2.1 定义

Java Virtual Machine Stacks (Java 虚拟机栈)

  • 每个线程运行时所需要的内存,称为虚拟机栈

  • 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存

  • 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法

问题辨析

1.垃圾回收是否涉及栈内存

  • 不涉及

2.栈内存分配越大越好吗

  • 不是,由于物理内存一定,栈内存越大会导致线程数变小。栈内存变大了通常只是能进行更多次的方法调用。

3.方法内的局部变量是否线程安全

  • 如果方法内局部变量没有逃离方法的作用范围,它是线程安全的

  • 如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全

2.2 栈内存溢出

栈帧过多导致栈内存溢出

栈帧过大导致栈内存溢出

例:方法递归调用未正常结束、循环引用

2.3 线程运行诊断

案例 1 : cpu 占用过多

定位

  • 用top定位哪个进程对cpu的占用过高

  • ps H -eo pid,tid,%cpu | grep 进程id (用ps命令进一步定位是哪个线程引起的cpu占用过高)

  • jstack 进程id

    • 可以根据线程id 找到有问题的线程,进一步定位到问题代码的源码行号

案例 2 :程序运行很长时间没有结果

  • 可能发生了死锁

 

3. 本地方法栈

为本地方法的调用提供内存空间

 

4. 堆

4.1 定义

Heap 堆

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

特点

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

  • 有垃圾回收机制

4.2 堆内存溢出

当对象一直被创建但又不能被垃圾回收时,可能会导致堆内存溢出

4.3 堆内存诊断

  1. jps 工具 查看当前系统中有哪些 java 进程

  2. jmap 工具 查看堆内存占用情况 :jmap - heap 进程id

  3. jconsole 工具 图形界面的,多功能的监测工具,可以连续监测

案例

  • 垃圾回收后,内存占用仍然很高

jvisualym工具:可视化方式展示jvm内容

 

5. 方法区

5.1 定义

JVM规范-方法区定义

方法区是所有java虚拟机线程共享的区域,存储了跟类的结构相关的信息(类的成员变量、方法数据、成员方法、构造器方法)、运行时常量池

方法区在虚拟机启动时被创建,逻辑上是堆的一个组成部分,规范中并不强制方法区的位置

5.2 组成

5.3 方法区内存溢出

  • 1.8 以前会导致永久代内存溢出

    • 演示永久代内存溢出 java.lang.OutOfMemoryError: PermGen space

    • -XX:MaxPermSize=8m

  • 1.8 之后会导致元空间内存溢出

    • 演示元空间内存溢出 java.lang.OutOfMemoryError: Metaspace

    • -XX:MaxMetaspaceSize=8m

类加载过多会导致方法区内存溢出

场景

  • mybatis

  • spring

 

5.4 运行时常量池

  • 常量池,就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息

  • 运行时常量池,常量池是 *.class 文件中的,当该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址

5.5 StringTable

先看几道面试题:

​
​
String s1 = "a";
String s2 = "b";
String s3 = "a" + "b"; //"ab"
​
String s4 = s1 + s2; //new String("ab")
String s5 = "ab";
String s6 = s4.intern(); 
//字符串常量池:"a","b","ab"
//堆内存:new String("ab")
// 问
System.out.println(s3 == s4);  //false
System.out.println(s3 == s5);  //true
System.out.println(s3 == s6);  //true
String x2 = new String("c") + new String("d"); //new String("cd") 
String x1 = "cd";
x2.intern();
//字符串常量池:"c","d","cd"
//堆内存:new String("c"),new String(d),new String("cd") 
// 问,如果调换了【最后两行代码】的位置呢,如果是jdk1.6呢
System.out.println(x1 == x2);
// StringTable [ "a", "b" ,"ab" ]  hashtable 结构,不能扩容
public class Demo1_22 {
    // 常量池中的信息,都会被加载到运行时常量池中, 这时 a b ab 都是常量池中的符号,还没有变为 java 字符串对象
    // ldc #2 会把 a 符号变为 "a" 字符串对象
    // ldc #3 会把 b 符号变为 "b" 字符串对象
    // ldc #4 会把 ab 符号变为 "ab" 字符串对象
​
    public static void main(String[] args) {
        String s1 = "a"; // 懒惰的
        String s2 = "b";
        String s3 = "ab";
        String s4 = s1 + s2; // new StringBuilder().append("a").append("b").toString()  new String("ab")
        String s5 = "a" + "b";  // javac 在编译期间的优化,结果已经在编译期确定为ab
​
        System.out.println(s3 == s5);
​
​
​
    }

 

5.5 StringTable 特性(字符串常量池)

  • 常量池中的字符串仅是符号,第一次用到时才变为对象

  • 利用串池的机制,来避免重复创建字符串对象

  • 字符串变量拼接的原理是 StringBuilder (1.8)

  • 字符串常量拼接的原理是编译期优化

  • 可以使用 intern 方法,主动将串池中还没有的字符串对象放入串池

    • 1.8 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回

    • 1.6 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份,放入串池, 会把串池中的对象返回

 

5.6 StringTable 位置

5.7 StringTable 垃圾回收

5.8 StringTable 性能调优

调整 -XX:StringTableSize=桶个数

考虑将字符串对象是否入池

6. 直接内存

6.1 定义

Direct Memory

常见于 NIO 操作时,用于数据缓冲区
分配回收成本较高,但读写性能高
不受 JVM 内存回收管理

6.2 分配和回收原理

使用了 Unsafe 对象完成直接内存的分配回收,并且回收需要主动调用 freeMemory 方法
ByteBuffer 的实现类内部,使用了 Cleaner (虚引用)来监测 ByteBuffer 对象,一旦
ByteBuffer 对象被垃圾回收,那么就会由 ReferenceHandler 线程通过 Cleaner 的 clean 方法调
用 freeMemory 来释放直接内存

This is a offline tool, your data stays locally and is not send to any server!

Feedback & Bug Reports

标签:ab,常量,线程,内存,jvm,结构,out,String
From: https://www.cnblogs.com/jonah-liu/p/16795063.html

相关文章

  • Java数据结构学习之栈Stack
    Stack学习背景:在刷算法题中有个模块是针对栈结构的算法,而且在很多代码底层总也用到栈stack的数据结构,所以在此简单记录一下。介绍:栈(Stack)是一种后进先出(LIFO:LastIn......
  • 数据结构:二叉树
    定义特点每个节点最多有两棵子树,所以二叉树中不存在度大于2的节点。左子树和右子树是有区别的,次序不能颠倒。即使某个节点只有1个子节点,也是有左右之分的。特殊的......
  • 数据结构—线性表的定义和特点
          在日常生活中,线性表的例子比比皆是。例如,26个英文字母的字母表:(A,B,C,...,Z)是一个线性表,表中的数据元素是单个字母。在稍复杂的线性表中一个数据元素可以包含若......
  • 计算机结构 week2
    记住单词:一些进制•Denary/Decimalorbaseten-tensymbols0,1,2,3,4,5,6,7,8,9•Binaryorbasetwo-twosymbols0,1•Hexadecimalorbase1......
  • 内存泄漏
    内存泄漏:当应用程序请求使用一段内存时,操作系统会为其分配特定的内存空间。当应用程序使用完内存后,应该通知操作系统释放内存,以便其他应用程序继续使用。但有些应用程序编......
  • 二叉树(存储结构,三种遍历方式,构建树)——C语言描述
    二叉树(存储结构,三种遍历方式,构建树)——C语言描述目录二叉树(存储结构,三种遍历方式,构建树)——C语言描述0测试用例框架1定义2特殊二叉树3二叉树的性质4二叉树存储结构5......
  • 动态内存分布
    在C中,不同数据在内存中分配说明: 动态内存分配的相关函数:   void*只是提供一个纯地址,不指向任何数据,如下图的*p3void指针类型: 即指针类型为void,不能用*p的......
  • 数据结构—第一章绪论习题
    1、简述下列概念:数据、数据元素、数据项、数据对象、数据结构、逻辑结构、存储结构、抽象数据类型。解答:    数据:是客观事物的符号表示,指所有能输入到计算机中并被......
  • 【C语言】函数调用操作符、结构成员访问操作符、隐私类型转换、操作数优先级大小。
    ......
  • 【C语言】函数调用操作符、结构成员访问操作符、隐私类型转换。
    ......