java基础(持续学习更新记录)

1、堆栈
1)内存分配策略

  • 静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编译时就可以给他们分配固定的内存空间.这种分配策略要求程序代码中不允许有可变数据结构(比如可变数组)的存在,也不允许有嵌套或者递归的结构出现,因为它们都会导致编译程序无法计算准确的存储空间需求.
  • 栈式存储分配也可称为动态存储分配,是由一个类似于堆栈的运行栈来实现的.和静态存储分配相反,在栈式存储方案中,程序对数据区的需求在编译时是完全未知的,只有到运行的时候才能够知道,但是规定在运行中进入一个程序模块时,必须知道该程序模块所需的数据区大小才能够为其分配内存.和我们在数据结构所熟知的栈一样,栈式存储分配按照先进后出的原则进行分配。
  • 静态存储分配要求在编译时能知道所有变量的存储要求,栈式存储分配要求在过程的入口处必须知道所有的存储要求,而堆式存储分配则专门负责在编译时或运行时模块入口处都无法确定存储要求的数据结构的内存分配,比如可变长度串和对象实例.堆由大片的可利用块或空闲块组成,堆中的内存可以按照任意顺序分配和释放.

以上说法来自于摘抄其他博客,我个人理解是,静态存储就是一些final的数据,编译时就知道了。堆式存储就是一些new出来的对象,数组这些不确定大小的东西。栈式存储就是,堆里面变量的引用,这个引用的存储大小是固定的,已知的,只有在进入程序块之前才能知道该块里有多少引用,需要分配多少空间来放引用

2)堆栈存储内容,回收机制

  • 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配。当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配的内存空间,该内存空间可以立刻被另作他用。
  • 堆内存用于存放由new创建的对象和数组。在堆中分配的内存,由java虚拟机自动垃圾回收器来管理。在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,在栈中的这个特殊的变量就变成了数组或者对象的引用变量
  • 引用变量是普通变量,定义时在栈中分配内存,引用变量在程序运行到作用域外释放。而数组&对象本身在堆中分配,即使程序运行到使用new产生数组和对象的语句所在地代码块之外,数组和对象本身占用的堆内存也不会被释放,数组和对象在没有引用变量指向它的时候,才变成垃圾,不能再被使用,但是仍然占着内存,在随后的一个不确定的时间被垃圾回收器释放掉。这个也是java比较占内存的主要原因

3)其他
Java应用都唯一对应一个JVM实例,每一个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由应用所有的线程共享。每一个线程对应一个栈空间。
跟C/C++不同,Java中分配堆内存是自动初始化的。Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在堆栈中分配,也就是说在建立一个对象时从两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在堆栈中分配的内存只是一个指向这个堆对象的指针(引用)而已。

参考地址:http://www.cnblogs.com/whgw/archive/2011/09/29/2194997.html

2.Java虚拟机内存模型
共享部分(每个JVM实例一份):
1)方法区:共享区域,存储类信息,方法信息,常量,静态变量,JIT编译后的代码等数据。
运行时常量池:在方法区内部,保存常量数据,存储字面量和符号引用。常量不仅仅在编译时产生,运行时也可以,比较常用的是String类的intern()方法。
关于符号引用说明,参考 https://blog.csdn.net/u014656992/article/details/51107127。
在class文件中引用的类是通过一个符号来表示,这些符号存储在常量池中。当创建一个对象或者使用到某个类时,在常量池中定位到该符号引用,是否直接引用到方法区的的内存,包含类定义,方法定义等。如果没有直接应用,会进行类加载的相关操作,符号引用改为直接引用。
2)堆,所有的对象分配的内存都是从这里分配的,GC回收的也是这里的内存。

不共享部分(每个线程一份,一个JVM实例包含多个线程)
1)程序计数器,保存当前执行到哪一行代码,如果是调用native方法,计数器数值保存的是undefined。
2)Java虚拟机栈,保存的是调用链,每调用一个方法会新建一个栈帧压入栈中,栈帧中包含请求参数,局部变量,返回值地址等等。等这个方法执行完就会删除。如果调用链过长,就会抛出StackOverflowError异常,如果栈帧申请的内存过大,就会出现OutOfMemoryError异常。
3)本地方法栈,类似于虚拟机栈,虚拟机栈保存的是纯java方法,本地方法栈保存的是native方法,除了java语言编写的都是native 方法。

3.Java对象创建
1、检查常量池中有没有对应的符号应用,对应的类有没有被加载,解释。
2、分配内存

  • 如果空闲内存是规整的,即在GC的时候整理过。存在一个指针分隔空闲空间和已分配空间,那么指针就移动相应大小的内存空间,这种叫“指针碰撞”; 如果内存是不规整的,那么虚拟机要维护一个空闲内存的列表,从列表中分配。
  • 分配指针的时候要保证原子性,一种是通过多次CAS加上失败重试保证原子性;一种是在堆在为每个线程预先分配一小块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer),哪个线程需要创建对象就在对应线程的TLAB上分配,只有TLAB满了,需要重新分配TLAB的时候才需要加锁同步。

3、设置对象头:对象头中存储对象运行时数据,如hashCode,GC分代,线程id等。可能存在类型数据指针,用于表明这个对象是什么类的对象。
4、设置对象的实例数据
5、设置对齐填充,仅作为占位操作,hotspot要求对象地址的起始位置是8字节的倍数,即要求对象大小是8字节的倍数
6、使用对象引用,找到对象:

  • 堆中分配一块内存作为句柄池,对象引用先指向句柄池中的一个对象,这个对象保存两个指针,一个是实例数据在堆中的地址,一个是类型数据在方法区中的地址,这个时候对象头中不一定要有类型数据的指针。这种方法提供了稳定的句柄指针,在移动数据的时候只要修改句柄中指向实例的指针,不需要修改引用
  • 对象引用直接指向直接堆中对象的地址,这个时候对象头中要保存类型数据的指针。好处是比句柄方式要快,减少了一次定位类型数据的时间开销。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值