Java内存区域
2020-05-21 本文已影响0人
lbcBoy
-
程序计数器
线程私有,生命周期与线程相同。
当前线程所执行字节码的行号指示器。
分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
没有OutOfMemoryjava的多线程是通过线程轮流切换和分配处理器执行时间的方式来实现的。
-
虚拟机栈
描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。 -
本地方法栈
本地方法栈为虚拟机使用的Native方法服务 -
堆 (线程共享)
唯一的目的:存放对象实例
可细分为新生代、老年代,再细致一点的有Eden空间、From Survivor空间、To Survivor空间等。
Java堆中可能划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB)问题:多线程中的ThreadLocal和TLAB什么关系? 答:不一样 TLAB(Thread Local Allocation Buffer) 1.堆是JVM中所有线程共享的,因此在其上进行对象内存的分配均需要进行加锁,这也导致了new对象的开销是比较大的。 2.Sun Hotspot JVM为了提升对象内存分配的效率,对于所创建的线程都会分配一块独立的空间TLAB(Thread Local Allocation Buffer)。 其大小由JVM根据运行的情况计算而得,在TLAB上分配对象时不需要加锁,因此JVM在给线程的对象分配内存时会尽量的在TLAB上分配 。 在这种情况下JVM中分配对象内存的性能和C基本是一样高效的,但如果对象过大的话则仍然是直接使用堆空间分配 3.TLAB仅作用于新生代的Eden Space,因此在编写Java程序时,通常多个小的对象比大的对象分配起来更加高效。 4.所有新创建的Object 都将会存储在新生代Yong Generation中。 如果Young Generation的数据在一次或多次GC后存活下来,那么将被转移到OldGeneration。 新的Object总是创建在Eden Space。
-
方法区(线程共享)
用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
运行时常量池是方法区的一部分。 -
直接内存(Direct Memory)
并不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域。
在JDK1.4中新加入了NIO类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。
**句柄 vs 直接指针**
句柄中指向的是到对象实例数据的指针;
直接指针中指向的是对象的实例数据;
他们本质都是指针,使用句柄带来的最大好处就是稳定,使用直接指针速度更快。
**内存溢出 vs 内存泄露**
内存泄漏(memory leak),在大型的、复杂的应用程序中,内存泄漏是常见的问题。
当以前分配的一片内存不再需要使用或无法访问时,但是却并没有释放它,那么对于该进程来说,会因此导致总可用内存的减少,这时就出现了内存泄漏。
尽管优秀的编程实践可以确保最少的泄漏,但是根据经验,当使用大量的函数对相同的内存块进行处理时,很可能会出现内存泄漏。
尤其是在碰到错误路径的情况下更是如此。
一般我们常说的内存泄漏是指堆内存的泄漏。
堆内存是指程序从堆中分配的,大小任意的(内存块的大小可以在程序运行期决定),使用完后必须显式释放的内存。
应用程序一般使用malloc,realloc,new等函数从堆中分配到一块内存,使用完后,程序必须负责相应的调用free或delete释放该内存块,
否则,这块内存就不能被再次使用,我们就说这块内存泄漏了。
总结:**内存泄露就是堆内存分配的对象空间,没有被GC正常回收,导致内存释放不了,最终会导致内存不足,溢出**