JVM——内存结构

内存结构

1.程序计数器

作用:记录程序执行的下一条指令。
特点:
1.线程私有
cpu给这个线程非配一段时间,但是这段时间没有执行完,换下一个,程序计数器记下下一条指令。每个线程都有自己的程序计数器。

2.虚拟机栈

1.什么是栈?

线程运行时需要的内存空间。

内存空间中:
方法区:存储.class的相关信息(执行进栈,new进堆)
堆:new出来的东西
栈:存储方法中的局部变量
1.

栈由多个栈帧组成。每个线程,只能有一个活动栈帧,对应着正在执行的那个方法。
栈帧:每个方法运行时需要的内存。方法;(参数,局部变量,返回值)

2.动态过程:

数组和学对象那里都讲过了!!自己复习。

3.线程问题:

1.垃圾回收是否涉及栈内存??
不需要。方法执行完,自动出栈,不需要垃圾回收来管理。垃圾回收回收的是堆内存中的无用对象。

2.栈的内存是否越大越好?
默认1024kb
栈内存太大,线程数变少。因为物理内存大小是一定的。栈帧*线程数(之和)=总大小(个人理解)一般使用默认就够了。

3.方法中的局部变量是否线程安全??
看它是否被线程共享(static),
或者当做参数,又被下一个方法使用,
或者他被当做返回值返回了,返回就意味着可能会被其他线程拿到,可能就被共享了。
只有被共享时才会存在安全问题。

注意:判断是否安全,不仅要看他是否是局部变量,还要看他是否 逃离了方法的舒服范围。

4.栈内存溢出(StackOverflowError)

导致栈内存溢出的情况:
1.栈帧只进不出就会导致,比如递归,一定要有终止条件。
2.栈帧过大
3.对象转json时 对象的两个类之间循环引用(设置@JsonIgnore,转换时忽略某个属性,解决这个问题)

自己测试:
如何设置栈内存:
idea中——Edit—— -Xxs256kb

5.线程诊断

1.cpu占用太高:
估计代码写的有问题了(如何排查 查看错误的进程编号 Linux:top命令 ps命令查看线程对cpu的占有情况)jstack 进程id(列出详细线程信息 但是怎么用????)
2.程序运行很长时间没有结果:
可能多个线程发生了死锁。
什么是死锁(deadLock)????两个锁相互嵌套(互锁)。

3.本地方法栈

调用本地方法时,提供的内存。本地方法指不是由java编写的代码。由c或c++编写的更底层的方法。就需要给他提供这种环境,间接调用native,通过接口。(比如hashcode,notify,wait等都是native方法)

4.堆(Heap):共享

注意:前面三个都是现成私有的。后面这两个是共享的。那么就要考虑现成安全问题。

1.安全问题:
2.垃圾回收问题:空闲对象,及时回收。System.gc():垃圾回收

那么为什么还会出现堆内存溢出(OutOfMemoryError)?生产太多,而且一直被用着。比如list加东西,被放入了死循环中。一直用着,停不下来。

-Xmx8m测试(想测试问题,就把内存改小一点)

1.堆内存诊断

jps:查看当前系统有哪些进程
jmap:查看堆内存占用情况

jhsdb jmap --heap --pid 13540

jconsole:连续化监测工具
hhhhhh 有点好玩

案例:执行多次垃圾回收后,内存占用率依然很高。
使用jvisualvm工具查找

5.方法区:共享

所有线程共享的区。

1.内存溢出

1.8之前:永久代内存溢出(类+类加载器+常量池(SstringTable))
1.8之后:(MetaSpace)元空间内存溢出(类+类加载器+常量池)(与之前不同的是,StringTable不在常量池中了,在堆中)

元空间使用的是系统内存(操作系统)

加载到栈时,栈空间不够了,就也是内存溢出了(OutOfMemoryError)

框架里,一般都会用到一些技术(cglib),用到一些代理类,动态产生class字节码技术,一不小心很肯能发生这种错误。

2.常量池

反编译:javap -v ***.class

字节码文件(类基本信息+常量池+类定义信息,包含了虚拟机指令)
常量池:(就是一张表)提供常量符号,虚拟指令根据常量表,去查自己执行的类名,方法名,参数类型等。

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

3.StringTable(面试题)

//注意:常量池中的信息,都会被加载到运行时常量池中,这时"a","b","ab"嗾使常量池中的符号,当被使用时才会变成java对象

字符串延迟加载:
 String s="ab";
        String s1="a";
        String s2="b";
        String s3="a"+"b";  //常量拼接,值是不可改变的,在编译期的时候结果就能确定,所以在常量池中
        String s4=s1+s2;  //两个变量拼接,值有可能改变,所以必须动态拼接,查看底层。
           //new StringBuilder.append("a").append("b").toString()    它的toString()方法里实际是 new String() 所以他存在在堆中


        System.out.println(s==s3);   //true
        System.out.println(s==s4);  //false

这里也要注意**==和equals的区别**:
1.8之后:
     String s1=new String("a")+new String("b");
        String s2 = s1.intern();  //把字符串对象放入串池中,如果串池中原本没有,
        // 则返回串池中的对象;如果有,不会放入

        System.out.println(s1=="ab"); //true
        System.out.println(s2=="ab");   //true

1.6:
     String s1=new String("a")+new String("b");
        String s2 = s1.intern();  //1.6是代表把堆中的复制了一份,把字符串对象放入串池中,如果串池中原本没有,
        // 则返回串池中的对象;如果有,不会放入

        System.out.println(s1=="ab"); //false
        System.out.println(s2=="ab");   //true

注意:1.s.intern()的位置很重要,池中没有,他就变成常量了,如果有,还是堆中的那个对象!!!!!
2.jdk1.6 1.8的区别:
3.左边是池中对象,右边是你自己入池不入池

   public static void main(String[] args) {
        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();

// 问
        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")
        x2.intern();
        String x1 = "cd";

// 问,如果调换了【最后两行代码】的位置呢,如果是jdk1.6呢
        System.out.println(x1 == x2);

StringTable位置:
在这里插入图片描述
1.8后:Minor Gc: 触发回收,用的堆空间
1.6:PermGen(永久代)才触发回收 内存中用到常量池很多,这就有点晚了。很容易触发内存不足。

测试StringTable发生内存溢出的情况:

jdk8: -Xmx10m  -XX:-UseGCOverheadLimit

-Xmx:堆内存

jdk6:-XX:MaxPermSize=10m

StringTable垃圾回收:

内存紧张时,会垃圾回收,一般都给放进去了。

演示:

-Xmx10m  -XX:PrintStringTableStatistics -XX:PrintGCDetails -verbose:gc

字符串常量也会被垃圾回收
底层类似于HashTable——桶机制

StringtTable:性能调优
与哈希表中的桶的个数密切相关

StringTable调优:1:实际就是调整桶的个数 更好的哈希分布 减少哈希冲突;2.考虑字符串是否入池(intern() 方法来调优)

什么是哈希冲突??
如何解决哈希冲突??

-Xsx500m -Xmx500m -XX:+PrintStringTableStatistics -XX:StringTableSize=200000

6.直接内存

不属于jvm,属于操作系统内存。那么他是否能够进行垃圾回收。
allocateDirect();可以回收。那么原理是什么呢??
有一个unsafe类:管理释放和回收直接内存,freeMemory()。

byteBuffer的关联——查看源码,里面有调用unsafe类

System.gc():显式垃圾回收。(回收新生代还有老年代,影响性能)
禁用显式回收对内存。(调优) 手动unsafe的freeMemory()释放。

-XX: +DisableExplictGC 禁用显式

  • 常用于NIO操作,用户数据缓冲区(Buffer)
  • 分配回收成本较高,但读写性能非常高
  • 不受jvm内存回收管理

什么是NIO??????

为什么Buffer或直接内存效率这么高???
java自己没有读写方法,必须调用操作系统的。
在这里插入图片描述
java不能直接操作,要进行读写。
那么Direct Memory(直接内存,划分出一片区域,java也可以用),少了从缓冲区的复制操作。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值