作者:虚线老母阳 | 来源:互联网 | 2023-10-10 13:17
java冷知识:spring的版权被控制在vmware手里,其实spring的那一大堆东西,本质上是一个非标准的jee实现,比如在jee里面用的inject,在spring里面就是autowire,当然spring曾经深刻滴影响了jee,所以有些东西比如di标准,是spring影响下制定出来的,所以spring的做法会比较特例一点。
1.直接内存
1.1Direct Memory
程序在运行时,不可避免的会访问系统内存,让我们来看一下jvm是如何帮我们进行处理的。
在jdk1.4中新加入了NIO类,引入了一种基于通道与缓存区的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,避免了在java堆和Native堆中来回复制数据。
操作系统的内存特点
java并不能直接操作系统内存,而是通过cpu内部的系统将磁盘信息读取到系统的缓存区,然后提供给java缓存区,由java调用。需要将缓存复制一遍。
分配的直接内存,操作系统和java都可以直接访问,减少了数据的访问时间和空间
1.2分配与回收原理
-
使用了Unsafe对象完成直接内存分配回收,并且回收需要主动调用freeMemory 方法
-
ByteBuffer的实现类内部,使用了Cleaner(虚引用)来检测ByteBuffer对象,一旦ByteBuffer对象被垃圾回收,那么就会由RefernceHandler线程通过Cleaner的clean方法调用freeMemory来释放直接内存
java中分配直接内存的方法(调用unsafe对象)
-
allocateMemory 返回分配内存的地址
-
setMemory
释放内存的方法(借助cleaner(虚引用类型),调用unsafe对象)
1.3OutOfMemoryError异常
本机直接内存的分配不受java堆大小的限制,但是,既然是内存,肯定还是回受本机总内存大小以及处理器寻址空间的限制。服务器管理员在配置虚拟机参数时,会根据实际内存设置-Xmx等参数信息,但经常忽略直接内存,使得各个内存区域总和大于物理内存限制,从而导致动态扩展时候回出现OutOfMemoryError异常。
2.对象的创建
对象是如何创建的呢?当我们去new一个对象的时候,虚拟机接收到new指令,会先去检查这个指令的参数是否在常量池中定位到一个类的符号引用,并检查符号引用代表的类是否已经被加载、解析和初始化,如果没有,则需要先加载。
2.1内存的分配方式
加载完之后,便是内存的分配,对象所需要的内存在类加载完成后便可完全确定。这里介绍两种分配方式
第一种:指针碰撞
指针碰撞看起来不是很好理解,其实很简单。一块完整的内存,所有已经分配的内存在一起,空闲的内存放在一起。当需要分配空间的时候,就将指针向空闲内存移动分配的空间大小。
第二种:空闲列表
顾名思义,空闲列表就是记录内些空闲区域的列表。java堆内存并不能保证已分配的内存和未分配的内存完全分开,所以用指针碰撞难以实现内存的分配。而空闲列表则会记录空闲的内存区域,当需要分配内存时,便到列表中找到一块足够的空间,并修改列表中的内容。
选用哪种分配方式,这由java堆是否工整决定的。
2.2线程问题
以上两种情况处理单线程时没有问题,但是当出现多线程时会出现这么一个问题。
当A线程正在给a分配内存,指针还未移动时,这时B线程给b分配内存,记录的指针初始位置与未分配a对象时的位置相同。
两种解决方案:
第一种:原子性,对内存分配空间进行同步处理。
第二种:为每一个线程分配“私有“的内存空间,这里的私有并不是广义上的私有 ,它还是分配的堆内存,只不过是将堆分配成多个空间,每一个线程占有一块空间,这里的私有空间我们称它为本地线程分配缓冲(TLAB)。只有当这块区域用完时,才会进行同步锁定。
是否使用TLAB可以通过-XX:+/-UseTLAB参数来设定。
内存分配完成后,会将分配的内存空间初始化为零(不包括对象头),这样在使用时,即使不赋值也可以直接使用。
对象头中还存有很多信息,比如这是哪个类的对象实例,如何找到元数据信息、对象的哈希码、对象的GC分带年龄等。
接下来便会执行方法,将对象初始化。
之前跟着黑马的老师刷过一遍jvm,讲的真的非常棒,可以结合这PDF和课程一起刷
学习视频链接:
黑马程序员JVM完整教程,全网超高评价,全程干货不拖沓_哔哩哔哩_bilibili
深入理解JVM网盘链接
链接:https://pan.baidu.com/s/1iiMsS3vBWbLOxXstE1EpEw
提取码:spoi