JVM——内存结构

阅读: 评论:0

JVM——内存结构

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.垃圾回收问题:空闲对象,及时回收。<():垃圾回收

那么为什么还会出现堆内存溢出(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);   //trueSystem.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"); //trueSystem.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"); //falseSystem.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"; // abString s4 = s1 + s2;   // new String("ab")String s5 = "ab";String s6 = s4.intern();// 问System.out.println(s3 == s4); // falseSystem.out.println(s3 == s5); // trueSystem.out.println(s3 == s6); // trueString 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类

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

-XX: +DisableExplictGC 禁用显式

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

什么是NIO??????

为什么Buffer或直接内存效率这么高???
java自己没有读写方法,必须调用操作系统的。

java不能直接操作,要进行读写。
那么Direct Memory(直接内存,划分出一片区域,java也可以用),少了从缓冲区的复制操作。

本文发布于:2024-01-31 07:14:54,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170665649926567.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:内存   结构   JVM
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23