JVM学习笔记(第一天)

2 篇文章 0 订阅
一、Java内存区域与内存溢出
学习目的:解决内存溢出与内存泄露

1、运行时数据区域

五大区域:

程序计数器(Program Counter Register):
        描述:可以看作当前线程所执行的字节码的行号指示器
        作用:通过改变计数器的值来实现改变一条需要执行的字节码指令,分支,循环,跳转,异常处理,线程恢复等基础功能
        特点:唯一一个不会发生OOM异常的一个区域;空间较小。
        是否线程私有:是

虚拟机栈(VM Stack):
        描述:每个方法在执行的同时都会创建一个栈桢用于存储信息。每个方法从调用到执行完成的过程,都是对应一个栈桢入栈到出栈的过程
        作用:存储方法的局部变量表、操作数栈、动态链接、方法出口等信息
        特点:生命周期与线程一致,
        是否线程私有:是
        异常:StackOverflowError,OutOfMemoryError
        空间分布:连续
        备注:局部变量表存放的是基本类型、引用类型(引用指针、句柄或相关位置)和returnAddress类型(指向了一条字节码指令的地址)

本地方法栈(Native Method Stack):
        描述:与虚拟机栈基本差不多,唯一不同的是他调用的是本地方法(Native服务)服务,而虚拟机栈则执行的是Java方法(字节码)服务
        作用:存储方法的局部变量表、操作数栈、动态链接、方法出口等信息
        特点:有些虚拟机会把本地方法栈与虚拟机栈合二为一。
        是否线程私有:是
        异常:StackOverflowErr or、OutOfMemoryError
        空间分布:连续    

(Heap):
        描述:几乎所有的对象以及数组都要在对上分配
        作用: 存放实例对象
        特点:使用空间最大的一块内存区域;在虚拟机创建时创建
        是否线程私有:否
        空间分布:不连续
        备注:被垃圾收集器管理的主要区域,又被称为GC堆;可细分为新生代和老年代,或者Eden空间、From Survivor和To Survivor;分配策略有分代收集算法。
        异常:OutOfMemoryError
        配置参数:-Xmx和-Xms

方法区(Method Area):
        描述:堆的一个逻辑部分
        作用:存储虚拟机加载的类信息、常量、静态变量、即时编译器后的代码等数据。
        特点:别名为Non—Heap(非堆)
        是否线程私有:否
        异常:OutOfMemoryError
        空间分布:不连续


2、运行时常量池(方法区的一部分)
        描述:Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译生成的各种字面量和符号引用
        作用:用于存放编译生成的各种字面量和符号引用,在运行时
        特点:内存受方法区内存限制;没有任何细节要求,不同的供应商实现的虚拟机可以 按照自己的需求来实现这个内存区域
        是否线程私有:否
        空间分布:否
         异常:OutOfMemoryError
        备注:java并不要求常量一定只有编译 才能产生,也就是并非预置入Class文件中常量池的内容才能进入方法区进行时常量池,运行时期间也可能将新的常量放入池中,如String的intern()方法。
        
        
3、直接内存
        描述:既不是java虚拟机运行时数据区域,也不是java虚拟机规范定义的内存区域
        作用:提高性能,跳出java虚拟机内存限制
        特点:受本机总内存(RAM和SWAP区或者分页文件)大小以及处理器寻址空间的限制
        异常:OutOfMemoryError
        空间分布:不连续,堆结构
        应用:由于这块内存不在JVM内存区域总和计算范围,所以单纯的考虑JVM内存小于总物理内存也会发生OutOfMemoryError异常,因此也要把直接内存考虑在内才是合理。
        备注: JDK1.4发布的NIO就是引入基于管道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在java堆的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景显著提高性能,因为避免了在java堆和Native堆中来回复制数据。


4、对象的创建
        new的创建过程:
                   (1) 双检查:检查这个指令的参数是否能在常量池中定位到一个类的符号引用并检查这个符号引用代表的类是否被加载、解析和初始化过,如果没有,那必须先执行相应的类加载过程。
                    
                    (2) 内存分配:在类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需内存的大小在内加载完成加载后便可完全、确定,为对象分配空间的任务的等同于把一块确认大小的内存从java堆中话划分出来。
                    ——内存分配的方式:(如何分配问题)
                    指针碰撞:在内存规整的情况下(即用过的内存在指针一边,空闲的内存在指正另一边),内存分配就仅仅是把指针向空闲空间挪动一小段与对象大小相等的距离,此时指针作为分界点的指示器,这种分配方式就是“指针碰撞”。

                    空间列表:在内存不规整的情况下(即用过的内存与空闲的内存相互交错),那就没办法简单地进行“指针碰撞”了,虚拟机必须维护一个列表来记录可用的内存块,在分配内存的时候从列表找到一块足够大的空间划分给对象实例,并更新列表,这种分配方式就是“空闲列表”。

                    注意:决定使用哪种分配方式是由内存分布是否规整来决定,而是否规整就由所采用的垃圾收集器是否带有压缩整理的功能决定。   有这功能的垃圾收集器有:Serial、ParNew等带Compact过程的收集器,因此采用的是“指针碰撞”;使用“空闲列表”的垃圾收集器就有基于Mark—Sweep算法的CMS收集器。

                    ——内存分配的并发问题:(分配是的线程不安全问题)
                    (直接同步锁定)对分配内存空间的动作进行同步处理:实际上虚拟机采用CAS配上失败重试的方式保证更新操作的原子性
                    (后同步锁定)使用本地线程分配缓冲:首先每个线程在java堆中都预先分配一小块内存(Thread Local Allocation Buffer,TLAB,即本地线程分配缓冲),当线程用完了并分配新的TLAB的时候才需要同步锁定
                     
                     配置项:可以通过-XX:+/-UseTLAB参数来设定
                     注意:内存分配完成后,虚拟机会把需要分配到的内存空间都初始化为零值(不包括对象头),如果使用了TLAB的话,初始化会提前在分配时同时进行,这一步操作保证了对象的实例字段在java代码可以不赋初始值就可以直接使用了,程序就能访问到这些字段对应数据类型的零值了。

                    (3) 初始化:按程序员的意愿进行初始化
                      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值