自动内存管理机制

对于Java程序员来说,在虚拟机自动内存管理机制的帮助下,不需要在为每个new操作去写配对的delete/free代码,不容易出现内存泄漏和内存溢出的问题,Java程序员把内存控制的权力交给了Java虚拟机

运行时数据区域

Java虚拟机在执行java程序的过程中会把他所管理的内存划分为若干个不同的数据区域
在这里插入图片描述

  1. 程序计数器(program counter register)
    概念:
    当前线程所执行的字节码的行号解释器

在虚拟机的概念模型里,字节码解释器工作时通过改变计数器的值选取下一条要执行的字节码指令

在物理上,通过寄存器来实现(读取快)

特点:
① 线程私有,每个线程有一个互不影响,用于线程切换后恢复到正确的执行位置
② 不存在内存溢出

  1. 虚拟机栈 (Java Virtual Machine Stacks)
    概念:
    栈:栈是线程运行时所需要的内存空间

栈帧:栈里面有一个一个的栈帧,栈帧是每个方法运行时所需的内存,所有的方法再执行时创建一个栈帧,用于存储① 局部变量表,② 操作数栈(对应当前的指令,栈帧对应的是方法),③ 动态链接, 方法出口等信息每调用执行一个方法就会往栈里面压入相应的栈帧,执行完再弹出

局部变量表:存放着编译期间可知的各个基本数据类型,对象引用和returnAddress类型(指向一条字节码指令的地址)

对应着当前正在执行的方法的栈帧叫做活动栈帧

特点:
① 线程私有

问题辨析:
① 垃圾回收不涉及栈内存:因为每次方法执行完,栈帧都会被弹出并清除

② 栈内存不是越大越好:因为物理内存时一定时,每个线程对应着一个栈,栈内存过大,所能划分出来的栈就少了

③ 栈里面的变量是线程安全的:线程私有的变量是线程安全的

线程安全问题:

在这里插入图片描述
这里的sb变量是一直在方法的作用范围,是线程安全的


在这里插入图片描述
这里的sb逃离了方法的作用范围,可能在其他线程做出改变,如
在这里插入图片描述


在这里插入图片描述
这里的sb也逃离了方法的作用范围,别的线程可能拿到并发的改变

所有对于如何判断变量是否线程安全
① 基本类型的变量可以保证线程安全
② 如果方法内部的局部变量没有逃离方法的作用范围,则是线程安全的

栈内存溢出
① 栈帧过多或栈帧过大导致,或者类的循环引用导致(SatckOverFlowError)
当栈的大小一定的使时候,线程所请求的栈的深度大于虚拟机所定的深度
如:不断的递归调用,使用ObjetMapper类转Json的时候类的循环引用

② OutOfMemoryError
当栈可以动态扩展的时候,如果扩展的时候无法申请到足够的内存

线程运行诊断(使用jstack进行诊断)
a. cpu占用过多
① 当我们的cpu占用过多的时候,排查问题,首先要去定位是哪个进程对cpu的占用高(Linux下使用top命令)
② 再去定位是哪个线程引起的cpu占用高(Linux下使用ps)
③ 再使用java提供的jsatck根据线程id找到有问题的线程,定位到问题代码的源码行数找到问题

b. 程序运行长时间得不到结果(可能是死锁问题)
还是使用jstack进行排查

本地方法栈

概念:
为虚拟机使用到的native方法服务
java native方法指的是本地方法,当在方法中调用一些不是由java语言写的代码或在方法中用java语言直接操作计算机硬件时要声明为native方法

如:对象的clone()方法就是通过直接调用c和c++进行实现
在这里插入图片描述
特点:
线程私有

Java堆

概念:
用于存放对象实例,是垃圾回收的主要区域,也别叫做GC堆
Java堆可以处于物理上不连续的内存区域,只要在逻辑上连续即可

特点:
① 线程共享,所以堆中的对象都会有线程安全问题
② 有垃圾回收机制

堆内存的回收问题(OutOfMemoryError)
为什么在堆有垃圾回收的情况下还会内存溢出:因为垃圾回收只回收不再被引用的对象,如果对象还在被引用,可能会溢出

例子:
在这里插入图片描述

堆内存的诊断
① jps工具:查看当前系统中的java进程

② jmap工具:查看堆内存的占用情况(某一时刻)
在这里插入图片描述
③ jconsole工具:图像界面的多功能检测工具,可以连续检测(terminal里面输入jconsole就可以打开)

④ jvirsualvm工具的dump工具直接查看堆的使用情况

方法区

概念:
用于存放已经被虚拟机加载的类信息(组成类的所有代码,类的方法,构造器等),常量,静态变量

特点:
线程共享的,虚拟机启动的时候创建,逻辑上是堆的一部分

对于1.6的内存结构中,用永久代作为方法区的实现
在这里插入图片描述
在1.8方法区被移除到本地内存中,不再由JVM管理,而且相对于1.6来说,串池StringTable被放到了堆中
在这里插入图片描述
方法区的内存溢出
在spring里面字节码的动态生成技术,在运行期间生成大量类,1.8以前会导致永久代的溢出,1.8以后会有元空间的溢出(系统的本地内存,不太容易溢出)

运行时常量池

在类编译成二进制字节码后,二进制字节码包含① 类的基本信息 ② 常量池 ③ 类的方法定义(包含了虚拟机指令)
可以使用javap -v对生成的二进制字节码进行反编译
在这里插入图片描述
常量池:放在二进制字节码文件,就是一张表,之后类的方法定义中的虚拟机指令根据这张表找到要执行的类名,方法名,参数类型,字面量等信息

运行时常量池:当类被加载(加载到内存),他的常量池信息就会被放入运行是常量池,并把里面的符号地址变为真实地址

StringTable

在这里插入图片描述
答案:false true true
false
调换顺序是true
jdk1.6是false

创建字符串对象两种方式的区别
String详解
① 直接赋值方式创建对象是在方法区的常量池

String str="hello";//直接赋值的方式

② 通过构造方法创建字符串对象是在堆内存

String str=new String("hello");//实例化的方式

StringTable和常量池的关系
以下面的代码为例:
在这里插入图片描述
字符串延迟加载
常量池中的信息都会被加载到运行时常量池,但还没有变成java的字符串对象
当运行到相应的代码时(String s1 = “a”),会优先在常量池中查找是否已经存在相同的字符串,倘若已经存在,栈中的引用直接指向该字符串;倘若不存在,则在常量池中生成一个字符串,再将栈中的引用指向该字符串

而对于s4 = s1 + s2
在这里插入图片描述
① 先创建一个StringBuilder对象
② 再调用StringBuilder的无参构造init()方法
③ 从局部变量表拿到两个参数s1,s2的值"a" "b"作为参数调用StringBuilderder.append()方法
④ 再对StringBuilderder.append()调用toString()方法
⑤ 而对于toString()方法是根据刚刚拼接好的值创建新的String对象,存入s4
在这里插入图片描述
也就是说s4 = s1 + s2 实际上对应的是 new StringBuilder().append(“a”).append(“b”).toString()
所以s4 != s3,是两个不同的对象

对于s5 = “a” + "b"
和s4 = s1 + s2,s1和s2是变量,编译期间无法确定,s4只能用StringBuilder进行创建新对象,且使用的是构造方法赋值,放在堆里,而s5 = “a” + “b"会进行编译期优化,两个拼接的值都是常量,所以拼接所得值一定是常量不会再变,编译期间就可以知道,所以编译生成的字节码是先去stringTable找"ab”

String s = new String(“a”) + new String(“b”)
① “a” "b"是常量,会被放入串池(常量放入串池,new出的对象放入堆)
② new String(“a”) 和 new String(“b”)是对象,放在堆中
③ String s也是new的对象,被放在堆中

使用intern()方法把字符串对象放入串池 ,如果串池中有了则不放入,并把串池中已有的对象返回,没有则放入(针对jdk1.8)

(对于jdk1.6)使用itern()方法,如果串池没有,是会把对象复制一份再放入串池

StringTable的位置

在这里插入图片描述
在jdk1.6的时候。StringTable是放在永久代,但是永久代只有在FULL GC才会进行垃圾回收,回收的频率很低,而String变量肯定是需要很高的回收频率的,所以在1.7 1.8就把StringTable放在了堆中, minor GC就会回收

什么是永久代(PermGen)

PermGen
方法区是JVM的一种规范,而PermGen是对这个规范的一种实现,只有 HotSpot 才有 “PermGen space”,而对于其他类型的虚拟机,如 JRockit(Oracle)、J9(IBM) 并没有“PermGen space”
元空间(Metaspace)是另外一种实现,元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存,并且把串池放到了堆里

那么为什么要做这么一个转变呢:
① 字符串存在永久代中,容易出现性能问题和内存溢出。

② 类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。

③ 永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。

StringTable的垃圾回收

内存空间不足的时候,StringTable里面没有被引用的字符串常量会被垃圾回收

StringTable调优//todo hash表

直接内存//todo NIO

发布了42 篇原创文章 · 获赞 0 · 访问量 537
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览