JAVA内存学习总结

从最开始学习java的时候,老师就讲过,java主要分为堆和栈两个内存区域,随着不断的学习和深入,也对java的内存有了更细致的了解。本文是个人通过以前老师所讲知识、查看各位大牛的博客,总结而来,纯属个人学习总结体会,不喜勿喷。

java内存划分

如上图所示,java内存主要分五个部分:

  1. 方法区(Method Area):方法区是各个线程共享的区域,用于存储已经被虚拟机加载的类信息(即加载类时需要加载的信息,包括版本、field、方法、接口等信息)、final常量、静态变量、编译器即时编译的代码等。比如String str = “const”;const就会存放在常量池中,当String strC = new String(“const”).intern(); str==strC 返回true。intern()方法会去常量池找到const这个常量,没有则重新生成const常量放入常量池。这里字符串常量才存到方法区的常量池,其他8种基本数据类型是不存放在这里的。
  2. 堆(Heap)对于java程序员再熟悉不过了,几乎所有的对象实例和数组是在堆中分配内存,它是被所有线程贡献的一块内存区域。GC主要管理的也是这部分区域,关于GC相关的在后续再说。堆分为:新生代和老年代。新生代又分为:Eden区,From Survivor、To Survivor区,默认比例为 8:1:1。通过参数-Xmx -Xms控制,最大和最小一般保持一致,避免动态扩容以及每次GC后重新分配内存。此区域内存不够时,会发生OutOfMemoryError。具体堆内存还可以细分:

这里细分主要涉及到垃圾回收机制时,后面有GC的详细介绍;

  1. Java虚拟机栈(JVM Stack):一个线程的每个方法在执行的同时,都会创建一个栈帧(Statck Frame),栈帧中存储的有局部变量表、操作站、动态链接、方法出口等,当方法被调用时,栈帧在JVM栈中入栈,当方法执行完成时,栈帧出栈。 局部变量表中存储着方法的相关局部变量,包括各种基本数据类型,对象的引用,返回地址等。在局部变量表中,只有long和double类型会占 用2个局部变量空间(Slot,对于32位机器,一个Slot就是32个bit),其它都是1个Slot。需要注意的是,局部变量表是在编译时就已经确定 好的,方法运行所需要分配的空间在栈帧中是完全确定的,在方法的生命周期内都不会改变。 一般来说,一个Java的引用访问涉及到3个内存区域:JVM栈,堆,方法区。 以最简单的本地变量引用:Object obj = new Object()为例: Object obj表示一个本地引用,存储在JVM栈的本地变量表中,表示一个reference类型数据; new Object()作为实例对象数据存储在堆中; 堆中还记录了Object类的类型信息(接口、方法、field、对象类型等)的地址,这些地址所执行的数据存储在方法区中; 在Java虚拟机规范中,对于通过reference类型引用访问具体对象的方式并未做规定,目前主流的实现方式主要有两种: 1.通过句柄访问(图来自于《深入理解Java虚拟机:JVM高级特效与最佳实现》):

通过句柄访问的实现方式中,JVM堆中会专门有一块区域用来作为句柄池,存储相关句柄所执行的实例数据地址(包括在堆中地址和在方法区中的地址)。这种实现方法由于用句柄表示地址,因此十分稳定。 2.通过直接指针访问:(图来自于《深入理解Java虚拟机:JVM高级特效与最佳实现》)(一般学习java基础的时候,老师都是讲的这种)

通过直接指针访问的方式中,reference中存储的就是对象在堆中的实际地址,在堆中存储的对象信息中包含了在方法区中的相应类型数据。这种方法最大的优势是速度快,在HotSpot虚拟机中用的就是这种方式。

  1. 本地方法栈(Native Method Statck):本地方法栈在作用,运行机制,异常类型等方面都与虚拟机栈相同,唯一的区别是:虚拟机栈是执行Java方法的,而本地方法栈是用来执行native方法的,在很多虚拟机中(如Sun的JDK默认的HotSpot虚拟机),会将本地方法栈与虚拟机栈放在一起使用。
  2. 程序计数器(Program Counter Register):程序计数器是一个比较小的内存区域,用于指示当前线程所执行的字节码执行到了第几行,可以理解为是当前线程的行号指示器。字节码解释器在工作时,会通过改变这个计数器的值来取下一条语句指令。 每个程序计数器只用来记录一个线程的行号,所以它是线程私有(一个线程就有一个程序计数器)的。 如果程序执行的是一个Java方法,则计数器记录的是正在执行的虚拟机字节码指令地址;如果正在执行的是一个本地(native,由C语言编写 完成)方法,则计数器的值为Undefined,由于程序计数器只是记录当前指令地址,所以不存在内存溢出的情况,因此,程序计数器也是所有JVM内存区 域中唯一一个没有定义OutOfMemoryError的区域。

至此,就对java中内存有了一个详细的了解,对后面的垃圾回收机制和性能优化打下基础。

参考博文:

Java内存区域概述

优秀Java程序员必须了解的GC工作原理

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏李蔚蓬的专栏

跨程序共享数据——Content Provider 之 ContentResolver基本用法 & 一个读取系统联系人的Demo

本模块共有四篇文章,参考郭神的《第一行代码》,对Content Provider的学习做一个详细的笔记,大家可以一起交流一下:

17420
来自专栏Hongten

python开发_tkinter_图片操作

在java的swing中,我们可以找到一些有关图片的操作,对于python的tkinter类似,也有对于图片的相关操作

12820
来自专栏李蔚蓬的专栏

小结:greenDAO和LitePal的区别

1. greenDAO的version等数据库属性设置都是在对应的模型类里面完成的,以Java class的属性变量的形式存储;而LitePal是在另外的一个x...

38610
来自专栏Hongten

python开发_dbm_键值对存储_完整_博主推荐

============================================

12950
来自专栏李蔚蓬的专栏

Android实战_note1(MyMirror_一款小型摄像处理的App)

1.1 Activity.java全文: 注意代码中的注释,其中 handler.sendEmptyMessageDelayed(1,3000);...

10820
来自专栏李蔚蓬的专栏

广播的最佳实践——实现强制下线功能(Android_Broadcast)

其一,用于辅助统筹管理本项目的所有活动。调用ActivityCollector类的方法,如类中的onCreate和onDestroy方法,所有从父类派生出去的子...

16530
来自专栏李蔚蓬的专栏

Content Provider 之 最终弹 实战体验跨程序数据共享(结合SQLiteDemo)

本模块共有四篇文章,参考郭神的《第一行代码》,对Content Provider的学习做一个详细的笔记,大家可以一起交流一下:

13540
来自专栏Danny的专栏

SLF4J和Logback日志框架详解

SLF4J是一套简单的日志外观模式的Java API,帮助在项目部署时对接各种日志实现。

27440
来自专栏李蔚蓬的专栏

Install Node.js

安装的话其实很容易,只要登陆上官网:https://nodejs.org/en/download/ 如下图:

25650
来自专栏李蔚蓬的专栏

java.lang.IndexOutOfBoundsException and drawPosText

郁闷。。报错的原因是数组超范围了。。 还以为是for语句写错了,看了半天。。。没错啊。。。

12520

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励