内存碎片

深入理解jvm jdk1,7(17)

你说的曾经没有我的故事 提交于 2020-03-31 08:59:06
垃圾收集算法 复制算法: 为了解决效率问题,一种称为“复制”的收集算法出现了,它将可用内存按容量划分为大小相等的两块,每次只使用其中一块。当这一块的内存用完了,就将还存活着对象复制到另一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也不用考虑内存碎片等复杂情况,只要一定堆顶指针,按顺序分配内存即可,实现简单,运行高效。只是这种算法的代价是将内存缩小为原来的一半,未免太高了一点。复制算法的执行过程如下图: 现在的商业虚拟机都采用这种收集算法来回收新生代,IBM公司的专门研究表明,新生代中的对象98%是“朝生夕死”的,所以并不需要按照1:1的比例来划分内存空间,而是将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一个Survivor。当回收时,将Eden和Survivor钟还存活这的对象一次性地复制到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。HotSpot虚拟机默认Eden和Survivor的大小比例是8:1,也就是每次新生代中可用内存空间为整个新生代容量的90%(80%+10%),只有10%的内存会被“浪费”。当然,98%的对象可回收只是一般的场景下的数据,我们没有办法保证每次回收都只有不多于10%的对象存活,当Survivor空间不够用时

Java面试常见知识点总结(一)

|▌冷眼眸甩不掉的悲伤 提交于 2020-03-30 16:00:55
1.sleep()和wait(): Java中的多线程是一种 抢占式 的机制,而不是分时机制。抢占式的机制是有多个线程处于可运行状态,但是只有一个线程在运行。 ● 共同点 : (1) 他们都是在 多线程 的环境下,都可以在程序的调用处阻塞指定的毫秒数,并返回。 (2) wait()和sleep()都可以通过 interrupt() 方法 打断线程的暂停状态 ,从而使线程立刻抛出InterruptedException。 如果线程A希望立即结束线程B,则可以对线程B对应的Thread实例调用interrupt方法。如果此刻线程B正在wait/sleep/join,则线程B会立刻抛出InterruptedException,在catch() {} 中直接return即可安全地结束线程。 需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛出的。对某一线程调用 interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。但是,一旦该线程进入到 wait()/sleep()/join()后,就会立刻抛出InterruptedException 。 ● 不同点 : (1) 每个对象都有一个锁来控制同步访问。 Synchronized 关键字可以和对象的锁交互

Java垃圾收集器

∥☆過路亽.° 提交于 2020-03-29 09:13:46
  概述   说起垃圾收集(Garbage Collection,GC),大部分人都把这项技术当做Java语言的伴生产物。事实上,GC的历史远远比Java久远,1960年诞生于MIT的Lisp是第一门真正使用内存动态分配和垃圾收集技术的语言。当Lisp还在胚胎时期时,人们就在思考:    GC需要完成的三件事情:      哪些内存需要回收?     什么时候回收?     如何回收?   经过半个世纪的发展,内存的动态分配与内存回收技术已经相当成熟,一切看起来都进入了“自动化”时代,那为什么我们还要去了解GC和内存分配呢?答案很简单:当需要排查各种内存溢出、内存泄漏问题时,当垃圾收集成为系统达到更高并发量的瓶颈时,我们就需要对这些“自动化”的技术实施必要的监控和调节。   把时间从半个世纪以前拨回到现在,回到我们熟悉的Java语言。第2章介绍了Java内存运行时区域的各个部分,其中程序计数器、虚拟机栈、本地方法栈三个区域随线程而生,随线程而灭;栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作。每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的(尽管在运行期会由JIT编译器进行一些优化,但在本章基于概念模型的讨论中,大体上可以认为是编译期可知的), 因此这几个区域的内存分配和回收都具备确定 性,在这几个区域内不需要过多考虑回收的问题,因为方法结束或线程结束时

Java虚拟机原理

北战南征 提交于 2020-03-29 07:55:49
1、编译机制 分析和输入到符号表: 词法分析:将代码转化为token序列 语法分析:由token序列生成抽象语法树 输入到符号表:将类中出现的符号输入到类的符号表 注解处理: 处理用户自定义注解,之后继续第一步 根据符号表进行语义分析并生成class文件,并进行相关优化 虚拟机数据类型、字节码文件格式、虚拟机指令集 2、执行机制 2.1、加载、链接、初始化 2.1.1、加载 双亲委派、线程上下文类加载器、Web容器、OSGi: http://www.ibm.com/developerworks/cn/java/j-lo-classloader/ 2.1.2、链接 校验:校验二进制字节码格式是否符合Java Class File Format规范 准备:为类的静态属性分配内存和默认值,并加载引用的类或接口 解析:将运行时常量池中的符号引用替换为直接引用(静态绑定) 2.1.3、初始化 类的初始化时机: 创建类的实例 初始化某个类的子类(满足主动调用,即访问子类中的静态变量、方法) 反射(Class.forName()会触发,ClassLoader.loadClass()及X.class不会触发) 访问类或接口的静态变量(static final常量除外,static final变量可以) 调用类的静态方法 java虚拟机启动时被标明为启动类的类 初始化顺序: 父类静态成员、静态代码块

.NET中栈和堆的比较(四)

故事扮演 提交于 2020-03-28 05:59:28
终于翻完了第四篇,本来每次都是周末发的,可惜上周末有些事儿没忙过来,所以今天中午给补上来。不知道这套文章还能不能继续了,因为作者也只写到了第四篇,连他都不知道第五篇什么时候出得来... 原文出处 http://www.c-sharpcorner.com/UploadFile/rmcochran/csharp_memory_401282006141834PM/csharp_memory_4.aspx 可以参看该系列文章的前面部分内容: Part I http://agassi001.cnblogs.com/archive/2006/05/10/396574.html Part II http://agassi001.cnblogs.com/archive/2006/05/13/399080.html Part III http://www.cnblogs.com/agassi001/archive/2006/05/20/405018.html 尽管在.NET framework下我们并不需要担心内存管理和垃圾回收(Garbage Collection),但是我们还是应该了解它们,以优化我们的应用程序。同时,还需要具备一些基础的内存管理工作机制的知识,这样能够有助于解释我们日常程序编写中的变量的行为。在本文中我们将深入理解垃圾回收器,还有如何利用静态类成员来使我们的应用程序更高效。 *

堆内存和栈内存的管理

六月ゝ 毕业季﹏ 提交于 2020-03-28 05:55:27
1、堆内存   堆内存是由程序员手工管理的,但它的申请是需要借助标准库函数。在大小上,理论上是物理内存的大小。关于堆内存的数据保存是靠程序员来管理的。由于是由程序员管理的,程序员的错误操作也导致内存的泄露和内存碎片的问题。   关于堆内存的标准库函数(stdlib.h/malloc.h)   void* malloc(size_t size);   //size是以字节为单位   //返回值是所申请到的内存的首地址   //void* 不能直接使用,需要转换成其他有意义的才能使用。   //在大多数情况下void*可以自转换成任意类型,但在个别情况下需要使用强制类型转换   //malloc函数申请的内存,内容其实是随机的,需要通过那个bzero函数来将它清理为0;   void bzero(void* s,size_t n); //功能是把内存清理为0,以字节为单位 //s是内存首地址,n字节数    void* calloc(size_t nmemb,size_t size); //size是所申请内存的字节数,nmemb是申请多次size,所申请到的内存内容已经被清理为0;   void* realloc(void* ptr,size_t size); //调整已经有内存的大小,可把内存调大或调小。 //ptr是已有的内存首地址,size是调整后的大小。 /

明晰C++内存分配的五种方法的区别

给你一囗甜甜゛ 提交于 2020-03-28 05:52:56
在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。   栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。   堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。   自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。   全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。   常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改,而且方法很多,在《const的思考》一文中,我给出了6种方法)   明确区分堆与栈   在bbs上,堆与栈的区分问题,似乎是一个永恒的话题,由此可见,初学者对此往往是混淆不清的,所以我决定拿他第一个开刀。   首先,我们举一个例子: void f() { int* p=new int[5]; }   这条短短的一句话就包含了堆与栈,看到new,我们首先就应该想到,我们分配了一块堆内存,那么指针p呢

明晰C++内存分配的五种方法的区别

和自甴很熟 提交于 2020-03-28 05:52:32
在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。    栈 ,就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区。里面的变量通常是局部变量、函数参数等。    堆 ,就是那些由new分配的内存块,它们的释放编译器不管,而是由程序员自己去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。    自由存储区 ,就是那些由malloc等分配的内存块,它和堆是十分相似的,不过它是用free来结束自己的生命的。    全局/静态存储区 ,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。    常量存储区 ,这是一块比较特殊的存储区,它们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改,而且方法很多,在《const的思考》一文中,我给出了6种方法) 明确区分堆与栈   在bbs上,堆与栈的区分问题,似乎是一个永恒的话题,由此可见,初学者对此往往是混淆不清的,所以我决定拿它第一个开刀。   首先,我们举一个例子: void f() { int* p=new int[5]; }   这条短短的一句话就包含了堆与栈,看到new,我们首先就应该想到,我们分配了一块堆内存

明晰C++内存分配的五种方法的区别

余生颓废 提交于 2020-03-28 05:49:34
在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。   栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。   堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。   自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。   全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。   常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改,而且方法很多,在《const的思考》一文中,我给出了6种方法)   明确区分堆与栈   在bbs上,堆与栈的区分问题,似乎是一个永恒的话题,由此可见,初学者对此往往是混淆不清的,所以我决定拿他第一个开刀。   首先,我们举一个例子: void f() { int* p=new int[5]; }   这条短短的一句话就包含了堆与栈,看到new,我们首先就应该想到,我们分配了一块堆内存,那么指针p呢

高性能-GC3

时光毁灭记忆、已成空白 提交于 2020-03-28 00:01:38
带着问题去思考!大家好 今天我们继续优化。 避免对象固定   对象固定(Pinning)是为了能够安全地将托管内存的引用传递给本机代码,最常见的用处就是 传递 数组和字符串 。如果不与本机代码进行交互,就完全不应该有对象固定的需求。 对象固定会把 内存的地址固定下来, 垃圾回收器就无法移动这些对象,会增加内存碎片的可能,垃圾回收器会记住那些被固定的对象,以便能利用固定对象之间的空闲内存。过多的情况,还会导致内存碎片的产生和内存堆的扩大   对象固定既可能是显式的,也可能是隐式的,使用GCHandleType.Pinned类型的GCHandle或者fixed关键字,可以完成显式对象固定,代码块必须标记为unsafe。用fixed/using用起来更方便,(fixed和GCHandle之间的区别类似于using和显式调用Dispose的差别),异步环境下是无法使用的,因为异步状态不能传递handle,也不能在回调方法中销毁handle。 避免使用终结方法   非必要情况下,永远不要实现终结方法(Finalizer).终结方法是一段由垃圾回收器引发调用的代码,用于清理非托管资源。终结方法由一个独立的线程调用,排成队列依次完成,而且只有依次垃圾回收之后,对象被垃圾回收器声明已销毁,才会进行调用。如果类实现了终结方法,对象就一定会滞留在内存中,即便在垃圾回收时应该被销毁的情况下。