a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 156|回复: 1

[基础知识] JAVA基础:JAVA虚拟机内存分配与回收机制说明

[复制链接]
发表于 2012-8-4 12:37:27 | 显示全部楼层 |阅读模式
Java把内存划分成两种:一种是栈内存,一种是堆内存。     在函数中界说的一些根基类型的变量和对象的引用变量都在函数的栈内存平分配。
1 d# p- E% [: i5 C  b    当在一段代码块界说一个变量时,Java就在栈中为这个变量分配内存空间,当跨越变量的浸染域后,Java会自动释放失踪为该变量所分配的内存空间,该内存空间可以当即被另作他用。
$ c" I, S$ W1 h' }& X/ ~    堆内存用来存放由new建树的对象和数组。9 K  b( k/ F& |6 D
    在堆平分配的内存,由Java虚拟机的自动垃圾收受接管器来打点。( n, y6 r1 N; L; {% `
    在堆中发生了一个数组或对象后,还可以在栈中界说一个非凡的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。! I- j3 ?1 d& i4 [5 L. h+ o/ c
    引用变量就相当于是为数组或对象起的一个名称,往后就可以在轨范中使用栈中的引用变量来访谒堆中的数组或对象。
2 g- ^( s7 V# `    具体的说:3 v2 Y) `. W" R5 @8 X  v
    栈与堆都是Java用来在Ram中存放数据的处所。与C++分歧,Java自动打点栈和堆,轨范员不能直接地设置栈或堆。
9 B. N* S5 ?8 |7 a! ~' I    Java的堆是一个运行时数据区,类的(对象从平分配空间。这些对象经由过程new、newarray、anewarray和multianewarray等 指令成立,它们不需要轨范代码来显式邓晔着。堆是由垃圾收受接管来负责的,堆的优势是可以动态地分配内存巨细,保留期也不必事先告诉编译器,因为它是在运行时 动态分配内存的,Java的垃收受接管集器会自动收走这些不再使用的数据。但错误谬误是,因为要在运行时动态分配内存,存取速度较慢。6 C8 N7 z' {) x; ]1 @' @
    栈的优势是,存取速度比堆要快,仅次于寄放器,栈数据可以共享。但错误谬误是,存在栈中的数据巨细与保留期必需是确定的,缺乏矫捷性。栈中首要存放一些根基 类型的变量(,int, short, long, byte, float, double, boolean, char)和对象句柄。% V7 K3 m; T6 Q
    栈有一个很主要的非凡性,就是存在栈中的数据可以共享。假设我们同时界说:! x0 c1 l! |6 C
    int a = 3;- ]% _/ K; i' D8 R/ W
    int b = 3;- G2 O1 N% w' v- \2 J- w
    编译器先措置int a = 3;首先它会在栈中建树一个变量为a的引用,然后查找栈中是否有3这个值,如不美观没找到,就将3存放进来,然后将a指向3。接着措置int b = 3;在建树完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就呈现了a与b同时均指向3的情形。这时,如不美观再令a=4;那么编译器 会从头搜索栈中是否有4值,如不美观没有,则将4存放进来,并令a指向4;如不美观已经有了,则直接将a指向这个地址。是以a值的改变不会影响到b的值。要注重这 种数据的共享与两个对象的引用同时指向一个对象的这种共享是分歧的,因为这种情形a的改削并不会影响到b, 它是由编译器完成的,它有利于节约空间。而一个对象引用变量改削了这个对象的内部状况,会影响到另一个对象引用变量。
, _6 e: C/ b3 w/ k3 G    String是一个非凡的包装类数据。可以用:
6 I& ?0 I9 r" ?: F8 k) k    String str = new String("abc");
/ D) z1 ~. Q% n; @( l    String str = "abc";
. d& o1 |  k' X* O( i    两种的形式来建树,第一种是用new()来新建对象的,它会在存放于堆中。每挪用一次就会建树一个新的对象。% L- D! R, T5 v: B5 V4 `% {

4 x) S3 Y1 [# t0 J) G    而第二种是先在栈中建树一个对String类的对象引用变量str,然后查找栈中有没有存放"abc",如不美观没有,则将"abc"存放进栈,并令str指向“abc”,如不美观已经有“abc” 则直接令str指向“abc”。
回复

使用道具 举报

 楼主| 发表于 2012-8-4 12:37:28 | 显示全部楼层

JAVA基础:JAVA虚拟机内存分配与回收机制说明

</p>    斗劲类琅缦沔的数值是否相等时,用equals()体例;当测试两个包装类的引用是否指向统一个对象时,用==,下面用例子声名膳缦沔的理论。
( {* J3 i2 [, G+ m    String str1 = "abc";
0 e! H# h! `; H8 T5 j. Z    String str2 = "abc";& ^; m8 t& y" a0 V  f5 d4 G
    System.out.println(str1==str2); //true可以看出str1和str2是指向统一个对象的。
/ b* c! T( \* o# b. [# X    String str1 =new String ("abc");2 ?/ g: I& U% V
    String str2 =new String ("abc");
7 ?0 C2 C. q$ o  C8 W; W6 Q! r    System.out.println(str1==str2); // false用new的体例是生成分歧的对象。每一次生成一个。( |$ d: }" F, w, i2 B; Q6 Q
    是以用第二种体例建树多个“abc”字符串,在内存中其实只存在一个对象而已. 这种写法有利与节约内存空间. 同时它可以在必然水平上提高轨范的运行速度,因为JVM会自动按照栈中数据的现实情形来抉择是否有需要建树新对象。而对于String str = new String("abc");的代码,则一概在堆中建树新对象,而不管其字符串值是否相等,是否有需要建树新对象,从而加重了轨范的承担。" |! s! Y7 ]8 v: b0 k
    另一方面, 要注重: 我们在使用诸如String str = "abc";的名目界说类时,老是想当然地认为,建树了String类的对象str。担忧陷阱!对象可能并没有被建树!而可能只是指向一个先前已经建树的 对象。只有经由过程new()体例才能保证每次都建树一个新的对象。 因为String类的immutable性质,当String变量需要经常变换其制瘫,应该考虑使用StringBuffer类,以提高轨范效率。
) e9 s+ w4 T/ G    java中内存分配策略及堆和栈的斗劲
( l9 ]; K$ X- f& X4 V    2.1 内存分配策略按照编译事理的不雅概念,轨范运行时的内存分配有三种策略,分袂是静态的,栈式的,和堆式的.静态存储分配是指在编译时就能确定每个数据方针在运行时刻的存储空间需求,因而在编译时就可以给他们分配固定的内存空间.这种分配策略要求轨范代码中不允 许有可变数据结构(好比可变数组)的存在,也不许可有嵌套或者递归的结构呈现,因为它们城市导致编译轨范无法计较切确的存储空间需求.栈式存储分配也可称为动态存储分配,是由一个近似于仓库的运行栈来实现的.和静态存储分配相反,在栈式存储方案中,轨范对数据区的需求在编译时是完全未知 的,只有到运行的时辰才能够知道,可是划定在运行中进入一个轨范模块时,必需知道该轨范模块所需的数据区巨细才能够为其分配内存.和我们在数据结构所熟知 的栈一样,栈式存储分配按照前进前辈后出的原则进行分配。+ b5 T5 r* ^$ P4 A& o0 H7 D& x
    静态存储分配要求在编译时能知道所有变量的存储要求,栈式存储分配要求在过程的进口处必需知道所有的存储要求,而堆式存储分配则专门负责在编译时或运行时 模块进口处都无法确定存储要求的数据结构的内存分配,好比可变长度串和对象实例.堆由大片的可操作块或余暇块组成,堆中的内存可以按照肆意挨次分配和释 放.
* `! N5 v5 q( z1 g6 N, g  j    2.2 堆和栈的斗劲9 S; o  [1 l0 T7 w. z# G
    膳缦沔的界说从编译事理的教材中总结而来,除静态存储分配之外,都显得很机械和难以理解,下面撇开静态存储分配,集中斗劲堆和栈:从堆和栈的功能和浸染来通俗的斗劲,堆首要用来存放对象的,栈主若是用来执行轨范的.而这种分歧又主若是因为堆和栈的特点抉择的:在编程中,例如C/C++中,所有的体例挪用都是经由过程栈来进行的,所有的局部变量,形式参数都是从栈平分配内存空间的。现实上也不是什么分配,只是从栈顶 向上用就行,就仿佛工场中的传送带(conveyor belt)一样,Stack Pointer会自动指引你到下班具的位置,你所要做的只是把工具放下来就行.退出函数的时辰,改削栈指针就可以把栈中的内容销毁.这样的模式速度最快, 当然要用来运行轨范了.需要注重的是,在分配的时辰,好比为一个即将要挪用的轨范模块分配数据区时,应事先知道这个数据区的巨细,也就说是虽然分配是在程 序运行时进行的,可是分配的巨细若干好多是确定的,不变的,而这个"巨细若干好多"是在编译时确定的,不是在运行时.堆是应用轨范在运行的时辰请求操作系统分配给自己内存,因为从操作系统打点的内存分配,所以在分配和销毁时都要占用时刻,是以用堆的效率很是低.可是堆的 利益在于,编译器不必知道要从堆里分配若干好多存储空间,也不必知道存储的数据要在堆里勾留多长的时刻,是以,用堆保留数据时会获得更大的矫捷性。事实上,面 向对象的多态性,堆内存分配是必不成少的,因为多态变量所需的存储空间只有在运行时建树了对象之后才能确定.在C++中,要求建树一个对象时,只需用 new呼吁编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保留.当然,为达到这种矫捷性,必然会支出必然的价钱:在堆里分配存储空间时会花 失踪更长的时刻!这也恰是导致我们适才所说的效率低的原因,看来列宁同志说的好,人的利益往往也是人的错误谬误,人的错误谬误往往也是人的利益.' j& }) g  c3 c7 C$ F+ q4 K- Q
    2.3 JVM中的堆和栈JVM是基于仓库的虚拟机.JVM为每个新建树的线程都分配一个仓库.也就是说,对于一个Java轨范来说,它的运行就是经由过程对仓库的操作来完成的。仓库以帧为单元保留线程的状况。JVM对仓库只进行两种操作:以帧为单元的压栈和出栈操作。6 _7 x" j  g; L& S0 G
    我们知道,某个线程正在执行的体例称为此线程的当前体例.我们可能不知道,当前体例使用的帧称为当前帧。当线程激活一个Java体例,JVM就会在线程的 Java仓库里新压入一个帧。这个帧自然成为了当前帧.在此体例执行时代,这个帧将用来保留参数,局部变量,中心计较过程和其他数据.这个帧在这里和编译 事理中的勾当记载的概念是差不多的.从Java的这种分配机制来看,仓库又可以这样理解:仓库(Stack)是操作系统在成立某个历程时或者线程(在撑持多线程的操作系统中是线程)为这个线程成立的存储区域,该区域具有前进前辈后出的特征。- b( t0 ]5 K! Z& g+ j+ B! m& O
    每一个Java应用都独一对应一个JVM实例,每一个实例独一对应一个堆。应用轨范在运行中所建树的所有类实例或数组都放在这个堆中,并由应用所有的线程 共享.跟C/C++分歧,Java平分配堆内存是自动初始化的。Java中所有对象的存储空间都是在堆平分配的,可是这个对象的引用却是在仓库平分配,也 就是耸ё仝成立一个对象时从两个处所都分配内存,在堆平分配的内存现实成立这个对象,而在仓库平分配的内存只是一个指向这个堆对象的指针(引用)而已。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Woexam.Com ( 湘ICP备18023104号 )

GMT+8, 2024-5-15 17:16 , Processed in 0.153980 second(s), 23 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表