jvm-内存结构

运行时数据区

运行时数据区也就是jvm在运行时产生的数据存放的区域,这块区域就是jvm的内存区域,也称为jvm的内存模型–JMM

在这里插入图片描述
在这里插入图片描述

程序计数器

作用:记住下一条jvm指令的执行地址
特点:
1.线程私有
2.唯一一个不会存在内存溢出

是一块就较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。
处于线程独占区
如果线程执行的是java代码,这个计数器记录的是正在执行的虚拟机字节码指令的地址。如果正在执行的是native方法,这个计数器的值为undefined
此区域是唯一一个在java虚拟机规范中没有规定任何OOM情况的区域
虚拟机栈

1.每个线程运行时所需要的内存,称为虚拟机栈
2.每个栈有多个栈帧组成,对应着每次方法调用时所占用的内存
3.每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法

线程栈(虚拟机栈):执行一个方法就会在线程栈中创建一个栈帧
栈帧:
    1.局部变量表: 存放方法中的局部变量,存放编译期可知的各种基本数据类型,引用类型,returnAddres类型
    2.操作数栈:用来存放方法中要操作的数据
    3.动态链接:存放方法名和方法内容的映射关系,通过方法名找到方法内容
    4.方法出口:记录方法执行完成后调用此方法的位置

问题辨析:
1.垃圾回收是否涉及栈内存
不涉及,
2.方法内的局部变量是否线程安全
如果方法内局部变量没有逃离方法的作用域,他是线程安全的。
如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全

栈内存溢出
1.栈帧过多导致栈内存溢出
2.栈帧过大导致栈内存溢出

线程运行诊断
案例1;cpu占用过多

nohup java com.yy.jvm.Demo01 &
# 第一步:使用 top 命令查看哪个进程占用CPU过高
top

# 第二步:查看某个进程中线程的CPU使用率, ps H -eo pid,tid,%CPU, | grep 进程ID
ps H -eo pid,tid,%CPU, | grep pid

# 第三步 列出某个进程下的所有线程, jstack 进程ID ,查看的线程都是16进制的
# 可以根据线程ID找到有问题的线程,进一步定位到问题代码的源代码行号
jstack pid

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

jstack 进程ID
本地方法栈

线程私有, 本地方法产生的数据

Heap 堆

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

  • 特点:
    1.线程共享的,堆中对象都需要考虑线程安全的问题
    2.有垃圾回收机制

堆内存诊断
1.jps工具

  • 查看当前系统中有哪些java进程

2.jmap工具

  • 查看堆内存占用情况(查看某个时刻的堆内存占用情况) -heap 进程ID
jmap -heap pid

3.jconsole工具

  • 图形界面的,多功能的检测工具,可以连续监测

4.jvisualvm

案例

  • 垃圾回收后,内存占用仍然很高
方法区
  • 1.8以前会导致永久代内存溢出
  • 1.8之后会导致元空间内存溢出

在这里插入图片描述

运行时常量池
  • 常量池:就是一张表,虚拟机指令根据这张常量表找到要执行的类名,方法名、参数类型、字面量等信息。
  • 运行时常量池:常量池是*.class文件中的。当该类被加载,它的常量池信息就会被放入运行时常量池,并把里面的符号地址变为真实地址

StringTable

  • 常量池中的字符串仅是符号,第一次用到时才变为对象
  • 利用串池的机制,来避免重复创建字符串对象
  • 字符串变量拼接的原理是 StringBuilder(1.8)
  • 字符串常量拼接的原理是编译期优化
  • 可以使用intern方法,主动将串池中还没有的字符串对象放入串池

注:StringBuilder 的 toString() 的调用,实际上调用的是 new String() 的方法,不会在常量池中创建字符串对象(动态拼接的字符串对象不会放在字符串常量池)


package com.yy.demo;
// StringTable ["a","b","ab"] hash table结构,不能扩容
public class Demo_001 {
    // 常量池中的信息,都会被加载到运行时常量池,这是 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");
        // System.out.println(s3 == s4); // false
        String s5 = "a" +"b"; // javac 在编译期间的优化,结果已经在编译期确定为ab
    }
}




package com.yy.demo;
public class Demo_003 {
    public static void main(String[] args) {
        // 堆 new String("a") new String("b") new String("ab");
        // 堆区,new StringBuilder().append("a").append("b").toString() -> new String("ab")
        String s = new String("a") + new String("b");
        // 将这个 "字符串对象" 尝试放入串池,如果有则不会放入,如果没有则放入串池,会把串池中的对象返回
        // ["a","b"]
        String s2 = s.intern() ;
        // ["a","b","ab"]
        System.out.println(s == s2); // true
        System.out.println(s == "ab"); // true
    }
}



在这里插入图片描述

StringTable 位置
在这里插入图片描述

StringTable 垃圾回收

StringTable 性能调优

  • 调整 -XX:StringTableSize=桶个数
  • 考虑将字符串对象是否入池
直接内存

操作系统内存

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

在这里插入图片描述
在这里插入图片描述


package com.yy.directmemory;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class Demo_001 {
    public static String FROM = "E:\\images\\rhel-server-7.4-x86_64-dvd.iso";
    public static String TO = "D:\\test\\rhel-server-7.4-x86_64-dvd_new.iso";
    public static int _1MB = 1024 * 1024;
    public static void main(String[] args) {
        directBuffer();
        io();
    }
    private static void directBuffer(){
        Long start = System.nanoTime();
        FileChannel from = null;
        FileChannel to = null;
        try {
            from = new FileInputStream(FROM).getChannel();
            to = new FileOutputStream(TO).getChannel();
            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1MB);
            while (true){
                int read = from.read(byteBuffer);
                if(read == -1){
                    break;
                }
                byteBuffer.flip();
                to.write(byteBuffer);
                byteBuffer.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(from != null){
                    from.close();
                }
                if(to != null){
                    to.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        Long end = System.nanoTime();
        System.out.println("directBuffer 用时:" + (end - start) / 10000_000.0);
    }
    private static void io() {
        Long start = System.nanoTime();
        FileInputStream from = null;
        FileOutputStream to = null;
        try{
            from = new FileInputStream(FROM);
            to = new FileOutputStream(TO);
            byte[] buf = new byte[_1MB];
            while (true){
                int read = from.read(buf);
                if(read == -1){
                    break;
                };
                to.write(buf,0,read);
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            try {
                if(from != null){
                    from.close();
                }
                if(to != null){
                    to.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        long end = System.nanoTime();
        System.out.println("IO 用时:" + (end - start) / 10000_000.0);
    }
}


分配和回收原理
  • 使用了Unsafe对象完成直接内存的分配回收,并且回收需要主动调用freeMemory方法
  • ByteBuffer 的实现类内部,使用了Cleaner(虚引用)来检测 ByteBuffer 对象,一旦ByteBuffer对象被垃圾回收,那么就会由 ReferenceHandle 线程通过 Cleaner 的clean 方法调用 freeMemory 来释放直接内存。
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值