按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的.
静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编译时就可以给他们分配固定的内存空间.这种分配策略要求程序代码中不允许有可变数据结构(比如可变数组)的存在,也不允许有嵌套或者递归的结构出现,因为它们都会导致编译程序无法计算准确的存储空间需求.
栈式存储分配也可称为动态存储分配,是由一个类似于堆栈的运行栈来实现的.和静态存储分配相反,在栈式存储方案中,程序对数据区的需求在编译时是完全未知的,只有到运行的时候才能够知道,但是规定在运行中进入一个程序模块时,必须知道该程序模块所需的数据区大小才能够为其分配内存.和我们在数据结构所熟知的栈一样,栈式存储分配按照先进后出的原则进行分配。
静态存储分配要求在编译时能知道所有变量的存储要求,栈式存储分配要求在过程的入口处必须知道所有的存储要求,而堆式存储分配则专门负责在编译时或运行时模块入口处都无法确定存储要求的数据结构的内存分配,比如可变长度串和对象实例.堆由大片的可利用块或空闲块组成,堆中的内存可以按照任意顺序分配和释放.
2.2 堆和栈的比较
上面的定义从编译原理的教材中总结而来,除静态存储分配之外,都显得很呆板和难以理解,下面撇开静态存储分配,集中比较堆和栈:
从堆和栈的功能和作用来通俗的比较,堆主要用来存放对象的,栈主要是用来执行程序的.而这种不同又主要是由于堆和栈的特点决定的:
在编程中,例如C/C++中,所有的方法调用都是通过栈来进行的,所有的局部变量,形式参数都是从栈中分配内存空间的。实际上也不是什么分配,只是从栈顶向上用就行,就好像工厂中的传送带(conveyor belt)一样,Stack Pointer会自动指引你到放东西的位置,你所要做的只是把东西放下来就行.退出函数的时候,修改栈指针就可以把栈中的内容销毁.这样的模式速度最快, 当然要用来运行程序了.需要注意的是,在分配的时候,比如为一个即将要调用的程序模块分配数据区时,应事先知道这个数据区的大小,也就说是虽然分配是在程序运行时进行的,但是分配的大小多少是确定的,不变的,而这个"大小多少"是在编译时确定的,不是在运行时.
堆是应用程序在运行的时候请求操作系统分配给自己内存,由于从操作系统管理的内存分配,所以在分配和销毁时都要占用时间,因此用堆的效率非常低.但是堆的优点在于,编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间,因此,用堆保存数据时会得到更大的灵活性。事实上,面向对象的多态性,堆内存分配是必不可少的,因为多态变量所需的存储空间只有在运行时创建了对象之后才能确定.在C++中,要求创建一个对象时,只需用 new命令编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保存.当然,为达到这种灵活性,必然会付出一定的代价:在堆里分配存储空间时会花掉更长的时间!这也正是导致我们刚才所说的效率低的原因,看来列宁同志说的好,人的优点往往也是人的缺点,人的缺点往往也是人的优点(晕~).
2.3 JVM中的堆和栈
JVM是基于堆栈的虚拟机.JVM为每个新创建的线程都分配一个堆栈.也就是说,对于一个Java程序来说,它的运行就是通过对堆栈的操作来完成的。堆栈以帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。
我们知道,某个线程正在执行的方法称为此线程的当前方法.我们可能不知道,当前方法使用的帧称为当前帧。当线程激活一个Java方法,JVM就会在线程的 Java堆栈里新压入一个帧。这个帧自然成为了当前帧.在此方法执行期间,这个帧将用来保存参数,局部变量,中间计算过程和其他数据.这个帧在这里和编译原理中的活动纪录的概念是差不多的.
从Java的这种分配机制来看,堆栈又可以这样理解:堆栈(Stack)是操作系统在建立某个进程时或者线程(在支持多线程的操作系统中是线程)为这个线程建立的存储区域,该区域具有先进后出的特性。
每一个Java应用都唯一对应一个JVM实例,每一个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由应用所有的线程共享.跟C/C++不同,Java中分配堆内存是自动初始化的。Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在堆栈中分配,也就是说在建立一个对象时从两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在堆栈中分配的内存只是一个指向这个堆对象的指针(引用)而已。
2.4 GC的思考
Java为什么慢?JVM的存在当然是一个原因,但有人说,在Java中,除了简单类型(int,char等)的数据结构,其它都是在堆中分配内存(所以说Java的一切都是对象),这也是程序慢的原因之一。
我的想法是(应该说代表TIJ的观点),如果没有Garbage Collector(GC),上面的说法就是成立的.堆不象栈是连续的空间,没有办法指望堆本身的内存分配能够象堆栈一样拥有传送带般的速度,因为,谁会为你整理庞大的堆空间,让你几乎没有延迟的从堆中获取新的空间呢?
这个时候,GC站出来解决问题.我们都知道GC用来清除内存垃圾,为堆腾出空间供程序使用,但GC同时也担负了另外一个重要的任务,就是要让Java中堆的内存分配和其他语言中堆栈的内存分配一样快,因为速度的问题几乎是众口一词的对Java的诟病.要达到这样的目的,就必须使堆的分配也能够做到象传送带一样,不用自己操心去找空闲空间.这样,GC除了负责清除Garbage外,还要负责整理堆中的对象,把它们转移到一个远离Garbage的纯净空间中无间隔的排列起来,就象堆栈中一样紧凑,这样Heap Pointer就可以方便的指向传送带的起始位置,或者说一个未使用的空间,为下一个需要分配内存的对象"指引方向".因此可以这样说,垃圾收集影响了对象的创建速度,听起来很怪,对不对?
那GC怎样在堆中找到所有存活的对象呢?前面说了,在建立一个对象时,在堆中分配实际建立这个对象的内存,而在堆栈中分配一个指向这个堆对象的指针(引用),那么只要在堆栈(也有可能在静态存储区)找到这个引用,就可以跟踪到所有存活的对象.找到之后,GC将它们从一个堆的块中移到另外一个堆的块中,并将它们一个挨一个的排列起来,就象我们上面说的那样,模拟出了一个栈的结构,但又不是先进后出的分配,而是可以任意分配的,在速度可以保证的情况下, Isn't it great?
但是,列宁同志说了,人的优点往往也是人的缺点,人的缺点往往也是人的优点(再晕~~).GC()的运行要占用一个线程,这本身就是一个降低程序运行性能的缺陷,更何况这个线程还要在堆中把内存翻来覆去的折腾.不仅如此,如上面所说,堆中存活的对象被搬移了位置,那么所有对这些对象的引用都要重新赋值.这些开销都会导致性能的降低.
此消彼长,GC()的优点带来的效益是否盖过了它的缺点导致的损失,我也没有太多的体会,Bruce Eckel 是Java的支持者,王婆卖瓜,话不能全信.个人总的感觉是,Java还是很慢,它的发展还需要时间.
分享到:
相关推荐
Java内存管理是编程中至关重要的一个环节,尤其是对于大型、长时间运行的应用来说,内存泄漏和内存无法回收可能导致系统性能下降,甚至导致系统崩溃。本文将深入探讨Java内存泄露的原理,分析内存无法回收的原因,并...
"JAVA内存管理模式研究" 在 Java 编程语言中,内存管理是非常关键的机制之一。 Java 虚拟机(JVM)通过对内存的分配和回收来管理内存资源。 本文将详细介绍 Java 的内存管理模式,包括内存区域、内存分配策略、垃圾...
### 如何解决Java内存泄漏 #### 1. 背景 Java凭借其垃圾回收机制大大简化了内存管理,使得开发者无需手动管理内存的释放,从而提升了开发效率。然而,这种自动化管理也可能成为一把双刃剑,特别是当开发人员忽视...
本篇文章将详细探讨jProfiler7在Java内存分析上的核心功能、使用方法以及在Linux环境中的配置和应用。 1. **内存分析概述** - 内存分析是识别和解决Java应用程序中的内存泄漏、过度对象创建和内存消耗过高问题的...
Java内存泄漏是一个严重的问题,它会导致程序性能下降,甚至可能导致应用程序崩溃。为了有效地诊断和解决这类问题,开发者需要借助特定的分析工具。本篇将详细探讨Java内存泄漏及其相关的分析工具。 内存泄漏是指...
Java内存分析是Java开发中非常重要的一个环节,它关乎到应用程序的性能和稳定性。"JAVA内存分析 - V1.0.0.zip" 提供了一套工具来帮助开发者深入理解并优化Java应用程序的内存使用情况。这个压缩包包含了两个关键文件...
### Java设计模式(刘伟) #### 一、引言 在《Java设计模式》这本书中,作者刘伟全面地介绍了24种经典的设计模式,并通过丰富的案例和代码示例进行了详细的解析。本书不仅适合初学者作为入门教材,也适合有一定...
在Java中,有多种实现单例模式的方法,每种都有其特点和适用场景。接下来,我们将深入探讨这些实现方式。 首先,我们来看**懒汉式(Lazy Initialization)**。这种实现方式是在类被首次请求时才创建单例对象,延迟...
Java 内存模型(Java Memory Model,简称 JMM)是 Java 平台中关于线程如何访问共享变量的一套规则,它定义了线程之间的内存可见性、数据一致性以及指令重排序等关键概念,对于多线程编程和并发性能优化至关重要。...
Java 内存调整是优化Java应用程序性能的关键环节,特别是对于处理大量对象和复杂数据结构的企业级应用。Java的内存管理主要依赖于垃圾回收机制,它自动处理对象的创建和销毁,但在某些情况下,不适当的内存配置和...
您是一个初级的 coder,可以从中领会到怎么设计一段优秀的...您是一个高级程序员,可以从中全面了解到设计模式以及 Java 的边角技术的使用;您是一个顶级的系统分析师,可以从中获得共鸣,寻找到项目公共问题的解决办法
MAT还提供了一些高级功能,比如"OQL"(Object Query Language),允许用户编写查询来查找特定的内存模式。此外,MAT的"Shallow Heap"和"Retained Heap"指标可以帮助你理解对象本身的大小以及它间接保留的内存大小。 ...
Java内存管理是Java核心技术的重要组成部分,对于每个开发者来说,理解其工作原理都是十分必要的。这一主题既实用又有趣。以下是对Java内存管理的精彩概述,主要基于Sun Hotspot JVM,但请注意,不同JVM可能有不同的...
根据提供的文件标题、描述、标签以及部分内容链接,我们可以推断出这篇文章主要关注的是Java中的23种设计模式,并且还涉及到了Debug模式和内存分析的相关内容。下面将围绕这些核心主题进行详细的阐述。 ### Java 23...
【使用JAVA内存数据库h2database性能优化】 在开发应用程序时,我们经常遇到性能瓶颈,特别是当涉及到大量的IO操作时。数据库访问是这类问题的主要来源,特别是在处理高并发、实时计算和海量数据监控的情况下。例如...
享元模式运用共享技术有效地支持大量细粒度的对象,减少内存占用。在Java中,字符串池就是一个典型的享元模式应用。 12. **命令模式(Command)** 命令模式将请求封装为一个对象,以便使用不同的请求、队列请求、...
Java设计模式是面向对象编程领域中的重要概念,对于学习Java的开发者来说,深入理解并掌握设计模式至关重要。设计模式是经过多次实践证明的有效解决方案的模板,可以帮助开发人员在解决复杂问题时快速找到合适的架构...
Java设计模式中的状态模式是一种行为设计模式,它允许对象在其内部状态改变时改变其行为,看起来好像对象改变了它的类。在提供的代码示例中,`CellContext2` 类实现了这一概念,通过`setState()`方法根据用户的余额...
Java内存模型(Java Memory Model, JMM)是Java平台中用于规范线程间通信和内存可见性的重要概念,它的目标是确保多线程环境下的正确同步。然而,原始的JMM存在一些严重的缺陷,导致了开发者在理解和实现线程安全时...