java开发学习笔记

  • 格式:docx
  • 大小:441.55 KB
  • 文档页数:26

java开发学习笔记GC/JVM/jiafu1115/article/details/7024323/zhguang/p/3257367.html#introductionHotSpot的GC机制。

运行时数据区包括:虚拟机栈区,堆区,方法区,本地方法栈,程序计数器虚拟机栈区:也就是我们常说的栈区,线程私有,存放基本类型,对象的引用和returnAddress ,在编译期间完成分配。

堆区,JAVA 堆,也称GC 堆,所有线程共享,存放对象的实例和数组,JAVA 堆是垃圾收集器管理的主要区域。

方法区:所有线程共享,存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。

这个区域的内存回收目标主要是针对常量池的对象的回收和对类型的卸载。

程序计数器:线程私有,每个线程都有自己独立的程序计数器,用来指示下一条指令的地址。

JVM堆(1) 新域:存储所有新成生的对象(2) 旧域:新域中的对象,经过了一定次数的GC循环后,被移入旧域(3)永久域:存储类和方法对象,从配置的角度看,这个域是独立的,不包括在JVM堆内。

默认为4M。

Java内存分配和回收的机制概括的说,就是:分代分配,分代回收。

对象将根据存活的时间被分为:年轻代(Young Generation)、年老代(Old Generation)、永久代(Permanent Generation,也就是方法区)新域会被分为3个部分:1.第一个部分叫Eden。

(伊甸园??可能是因为亚当和夏娃是人类最早的活动对象?)2.另两个部分称为辅助生存空间(幼儿园),我这里一个称为A空间(From sqace),一个称为B空间(To Space)“停止-复制(Stop-and-copy)”清理法(将Eden区和一个Survivor中仍然存活的对象拷贝到另一个Survivor中),绝大多数刚创建的对象会被分配在Eden区,其中的大多数对象很快就会消亡。

Eden区是连续的内存空间,因此在其上分配内存极快;最初一次,当Eden区满的时候,执行Minor GC,将消亡的对象清理掉,并将剩余的对象复制到一个存活区Survivor0(此时,Survivor1是空白的,两个Survivor总有一个是空白的);下次Eden区满了,再执行一次Minor GC,将消亡的对象清理掉,将存活的对象复制到Survivor1中,然后清空Eden区;将Survivor0中消亡的对象清理掉,将其中可以晋级的对象晋级到Old区,将存活的对象也复制到Survivor1区,然后清空Survivor0区;当两个存活区切换了几次(HotSpot虚拟机默认15次,用-XX:MaxTenuringThreshold控制,大于该值进入老年代,但这只是个最大值,并不代表一定是这个值)之后,仍然存活的对象(其实只有一小部分,比如,我们自己定义的对象),将被复制到老年代把所有对象组成一个集合,或可以理解为树状结构,,基于coping算法的垃圾收集就从根集中扫描活动对象,只要可以找到的都是活动对象,如果找不到,这个对象就是凋零的昨日黄花,应该被回收总结从上面的推导可以得出很多结论,下面是前辈的经验总结与自已的认识1.JVM堆的大小决定了GC的运行时间。

如果JVM堆的大小超过一定的限度,那么GC的运行时间会很长。

2.对象生存的时间越长,GC需要的回收时间也越长,影响了回收速度。

3.大多数对象都是短命的,所以,如果能让这些对象的生存期在GC的一次运行周期内,wonderful!4.应用程序中,建立与释放对象的速度决定了垃圾收集的频率。

5.如果GC一次运行周期超过3-5秒,这会很影响应用程序的运行,如果可以,应该减少JVM 堆的大小了。

6.前辈经验之谈:通常情况下,JVM堆的大小应为物理内存的80%。

GC算法1.引用计数器算法:引用计数器算法是给每个对象设置一个计数器,当有地方引用这个对象的时候,计数器+1,当引用失效的时候,计数器-1,当计数器为0的时候,JVM就认为对象不再被使用,是“垃圾”了。

引用计数器实现简单,效率高;但是不能解决循环引用问问题(A对象引用B对象,B对象又引用A对象,但是A,B对象已不被任何其他对象引用),同时每次计数器的增加和减少都带来了很多额外的开销,所以在JDK1.1之后,这个算法已经不再使用了。

2.根搜索方法:根搜索方法是通过一些“GCRoots”对象作为起点,从这些节点开始往下搜索,搜索通过的路径成为引用链(Reference Chain),当一个对象没有被GC Roots的引用链连接的时候,说明这个对象是不可用的。

GC Roots对象包括:a)虚拟机栈(栈帧中的本地变量表)中的引用的对象。

b)方法区域中的类静态属性引用的对象。

c)方法区域中常量引用的对象。

d)本地方法栈中JNI(Native方法)的引用的对象。

了解了JVM是怎么确定对象是“垃圾”之后,进入正题,让我们来看看垃圾回收的算法。

1.标记—清除算法(Mark-Sweep)标记—清除算法包括两个阶段:“标记”和“清除”。

在标记阶段,确定所有要回收的对象,并做标记。

清除阶段紧随标记阶段,将标记阶段确定不可用的对象清除。

标记—清除算法是基础的收集算法,标记和清除阶段的效率不高,而且清除后回产生大量的不连续空间,这样当程序需要分配大内存对象时,可能无法找到足够的连续空间。

垃圾回收前:垃圾回收后:绿色:存活对象红色:可回收对象白色:未使用空间2.复制算法(Copying)复制算法是把内存分成大小相等的两块,每次使用其中一块,当垃圾回收的时候,把存活的对象复制到另一块上,然后把这块内存整个清理掉。

复制算法实现简单,运行效率高,但是由于每次只能使用其中的一半,造成内存的利用率不高。

现在的JVM用复制方法收集新生代,由于新生代中大部分对象(98%)都是朝生夕死的,所以两块内存的比例不是1:1(大概是8:1)。

垃圾回收前:垃圾回收后:绿色:存活对象红色:可回收对象白色:未使用空间3.标记—整理算法(Mark-Compact)标记—整理算法和标记—清除算法一样,但是标记—整理算法不是把存活对象复制到另一块内存,而是把存活对象往内存的一端移动,然后直接回收边界以外的内存。

标记—整理算法提高了内存的利用率,并且它适合在收集对象存活时间较长的老年代。

垃圾回收前:垃圾回收后:绿色:存活对象红色:可回收对象白色:未使用空间4.分代收集(Generational Collection)分代收集是根据对象的存活时间把内存分为新生代和老年代,根据个代对象的存活特点,每个代采用不同的垃圾回收算法。

新生代采用标记—复制算法,老年代采用标记—整理算法。

垃圾算法的实现涉及大量的程序细节,而且不同的虚拟机平台实现的方法也各不相同。

上面介绍的只不过是基本思想。

java虚拟机功能(1)通过ClassLoader 寻找和装载class 文件(2)解释字节码成为指令并执行,提供class 文件的运行环境(3)进行运行期间垃圾回收(4)提供与硬件交互的平台java.exe是java虚拟机javadoc.exe用来制作java文档jdb.exe是java的调试器abstract interface 区别1.抽象类可以有构造方法,接口中不能有构造方法。

2.抽象类中可以有普通成员变量,接口中没有普通成员变量3.抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。

4. 抽象类中的抽象方法的访问类型可以是public,protected和(默认类型,虽然eclipse下不报错,但应该也不行),但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。

5. 抽象类中可以包含静态方法,接口中不能包含静态方法6. 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。

7. 一个类可以实现多个接口,但只能继承一个抽象类。

下面接着再说说两者在应用上的区别:接口更多的是在系统架构设计方法发挥作用,主要用于定义模块之间的通信契约。

而抽象类在代码实现方面发挥作用,可以实现代码的重用,wait sleep区别1、这两个方法来自不同的类分别是,sleep来自Thread类,和wait来自Object类。

sleep是Thread的静态类方法,谁调用的谁去睡觉,即使在a线程里调用了b的sleep方法,实际上还是a去睡觉,要让b线程睡觉要在b的代码中调用sleep。

2、最主要是sleep方法没有释放锁,而wait方法释放了锁,使得敏感词线程可以使用同步控制块或者方法。

sleep不出让系统资源;wait是进入线程等待池等待,出让系统资源敏感词线程可以占用CPU。

一般wait不会加时间限制,因为如果wait线程的运行资源不够,再出来也没用,要等待敏感词线程调用notify/notifyAll唤醒等待池中的所有线程,才会进入就绪队列等待OS分配系统资源。

sleep(milliseconds)可以用时间指定使它自动唤醒过来,如果时间不到只能调用interrupt()强行打断。

Thread.Sleep(0)的作用是“触发操作系统立刻重新进行一次CPU竞争”3、使用范围:wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用synchronized(x){x.notify()//或者wait()}4、sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常ReentrantLock /synchronizedServlet 线程安全问题Servlet体系结构是建立在Java多线程机制之上的,它的生命周期是由Web容器负责的。

当客户端第一次请求某个Servlet时,Servlet容器将会根据web.xml配置文件实例化这个Servlet 类。

当有新的客户端请求该Servlet时,一般不会再实例化该Servlet类,也就是有多个线程在使用这个实例。

Servlet 容器会自动使用线程池等技术来支持系统的运行由于Servlet/JSP默认是以多线程模式执行的,所以,在编写代码时需要非常细致地考虑多线程的安全性问题。

Servlet的线程安全问题只有在大量的并发访问时才会显现出来,并且很难发现,因此在编写Servlet程序时要特别注意。

线程安全问题主要是由实例变量造成的,因此在Servlet中应避免使用实例变量。

如果应用程序设计无法避免使用实例变量,那么使用同步来保护要使用的实例变量,但为保证系统的最佳性能,应该同步可用性最小的代码路径。