安徽省铜陵市建设银行网站,电子商务网站建设需求表,做网站百度推广,网络推广策划思路目录
Java
集合
集合概述
HashMap
ConcurrentHashMap
泛型
反射
注解
IO流
异常、深浅拷贝与Java8新特性
Java异常
深浅拷贝
Java8新特性
并发
线程
线程池
锁
volatile
JVM
内存区域
内存模型
类加载机制
垃圾回收机制
如何判断对象已死 Java
集合
…目录
Java
集合
集合概述
HashMap
ConcurrentHashMap
泛型
反射
注解
IO流
异常、深浅拷贝与Java8新特性
Java异常
深浅拷贝
Java8新特性
并发
线程
线程池
锁
volatile
JVM
内存区域
内存模型
类加载机制
垃圾回收机制
如何判断对象已死 Java
集合
集合概述
集合分类大致分为List、Set、Map和Queue四种体系Set代表无序、不可重复的集合List代表有序、重复的集合Map代表具有映射关系的集合Queue代表一种队列集合实现集合和数组区别a、长度数组长度在初始化时指定而集合可以保存数量不确定的数据同时可以保存具有映射关系的数据b、元素类型数组元素既可以是基本类型的值也可以是对象而集合里只能保存对象(实际上只是保存对象的引用变量)基本数据类型要转换成对应包装类才能放入集合类中集合类关系Collection和Map是Java集合框架的根接口List(List集合里增加了一些根据索引来操作集合元素的方法)、Set(Set集合与Collection集合基本相同没有提供任何额外方法实际上Set就是Collection只是Set不允许包含重复元素)和Queue(Queue模拟队列这种“先进先出”数据结构通常队列不允许随机访问队列中元素)接口继承自CollectionMap里所有key相当于一个Set集合、所有value相当于一个List集合ArrayList与LinkedListArrayList以数组实现查询快增删慢自动扩容动态数组节约空间但数组有容量限制默认第一次插入元素时创建大小为10的数组超出限制时会增加50%容量用 System.arraycopy()复制到新的数组因此最好能给出数组大小的预估值LinkedList以双向链表实现node函数将节点访问复杂度由O(n)变为O(n/2)链表无容量限制但双向链表本身使用了更多空间也需要额外的链表指针操作
HashMap
核心数组(哈希表)链表/红黑树基于Map接口实现、允许null键/值、非同步、不保证有序(比如插入的顺序)、也不保证顺序不随时间变化容量和负载因子容量Capacity容器大小默认16、负载因子loadFactor容器填满程度的最大比例默认0.75当容器内容数目大于 capacity*loadFactory时就需要调整容器大小为16的2倍为什么扩容并且为2的倍数数据结构是数组需要扩容如果是2的倍数就可以用位运算替代取余操作更加高效HashMap存取时计算index即(length- 1) hash使用运算符相比%效率更高如果length为2的倍数可以最大程度确保index均分put函数a、首先对key的hashCode()做hash然后再计算indexb、如果没碰撞直接放到bucket里c、如果碰撞了以链表的形式存在bucketsd、如果碰撞导致链表过长(大于等于8 )就把链表转换成红黑树(提高遍历查询)当红黑树上节点数量小于6个会重新把红黑树变成单向链表数据结构e、如果节点已经存在就替换old value(保证key唯一性)f、如果bucket满了(超过 load factor*current capacity )就要resizeget函数a、bucket里的第一个节点直接命中b、如果有冲突则通过key.equals(k)去查找对应的entryc、若为树则在树中通过key.equals(k)查找O(logn)若为链表则在链表中通过key.equals(k)查找O(n)hash函数a、在get和put的过程中计算下标时先对hashCode进行hash()操作然后再通过hash值进一步计算下标(n-1)hashb、hash()操作中hashcode()的高16bit不变低16bit和高16bit做异或(在n - 1为15(0x1111)时其实散列真正生效的只是低4bit的有效位容易碰撞设计者于是从速度、功效、质量考虑这么做可以在bucket的n比较小时也能保证考虑到高低bit都参与到hash的计算 同时不会有太大的开销)c、哈希冲突解决Java8之前使用链表O(n)Java8中采用红黑树O(logn)(现在大多数的hashCode的分布已经很不错)HashMap为什么使用String、int作为keya、都是final修饰的类能够保证Hash值的不可更改性和计算准确性b、内部已重写了equals()、hashCode()等方法能够有效的减少Hash碰撞Integer的hashCode就是值本身HashMap与Hashtable有什么区别a、Hashtable线程安全效率低HashMap非线程安全(多个线程同时resize可能会引起死循环、put操作覆盖)效率高b、null可以作为HashMap的key和value不可以作为Hashtable的keyc、Hashtable初始容量11负载因子0.75扩容2n1d、Hashtable没有链表转红黑树的机制HashMap与SpareArraykey为int时考虑使用SpareArraySpareArray使用2个数组存储key和value。好处a、没有自动拆装箱b、数据量不大时采用二分查找效率高不需要开辟内存空间来额外存储外部映射节省内存LinkedHashMap数组(哈希表)双向链表/红黑树双向链表能够保证访问顺序内部使用LruCache做最近最少使用的移除继承自HashMapput函数在LinkedHashMap中未重新实现只是实现了afterNodeAccess和afterNodeInsertion两个回调函数get函数则重新实现并加入了afterNodeAccess来保证访问顺序HashMap、LinkedHashMap、TreeMapHashMap不保证数据有序 LinkedHashMap保证数据插入顺序TreeMap保证Map的key大小顺序
ConcurrentHashMap
核心Java8的实现已经抛弃了Segment分段锁机制利用CASSynchronized来保证并发更新的安全底层依然采用数组table链表红黑树的存储结构解决问题HashMap多线程put操作并发死循环问题以及Hashtable、Collections.synchronizedMap(hashMap)加锁效率低问题数组table初始化不会在构造时初始化会延缓到第一次put行为第一次put操作的线程会执行Unsafe.compareAndSwapInt方法修改sizeCtl(默认0)为-1有且只有一个线程能够修改成功其它线程通过Thread.yield()让出CPU时间片等待table初始化完成数组table扩容构建一个nextTable大小为table两倍把table数据复制到nextTable中put函数采用CASsynchronized实现并发插入或更新操作key和value均不能为nullget函数a、判断table是否为空如果为空直接返回nullb、计算key的hash值并获取指定table中指定位置的Node节点通过遍历链表或则树结构找到对应的节点返回value值
泛型
泛型含义泛型本质就是参数化类型这种类型可以用在类、接口和方法的创建中分别称为泛型类、泛型接口、泛型方法是一种语法糖自动拆装箱、变长参数、内部类等也是语法糖泛型好处让类型更加安全。a、编译时类型检查将错误暴露在编译期不用等到运行时有助于开发者更容易找到错误提高程序可靠性b、运行时自动类型转换避免强制类型转换出现ClassCastExceptionc、更加语义化(比如List清楚知道存储的是String对象)和能写出更加通用化的代码(引入泛型后并未增加代码的冗余性)类型通配符上限List表示集合中所有元素都是Shape类型或其子类、下限List表示集合中所有元素都是Circle类型或其父类泛型擦除a、Java泛型是不变的(Fruit是Apple父类但List不是List父类)是类型擦除的可以看做伪泛型b、无法在程序运行时获取到一个对象的具体类型c、在静态方法、静态初始化块或静态变量的声明和初始化中不允许使用类型形参d、由于系统中并不会真正生成泛型类所以instanceof运算符后不能使用泛型类
反射
反射机制运行时动态获取类信息以及动态调用对象反射机制应用场景a、逆向代码(反编译)b、与注解相结合的框架(Retrofit)c、单纯的反射机制应用框架(EventBus)d、动态生成类框架(Gson)3种方式获取Class对象a、Class.forName(全限定类名)b、类名.classc、对象.getClass()反射APIa、getDeclaredFields与getFields——获取所有属性与获取所有public属性b、getDeclaredMethods与getMethods——所有方法与所有public方法c、getDeclaredConstructors与getConstructors——所有构造方法与所有public构造方法d、getAnnotation(Deprecated.class)——获取指定注解e、newInstance——创建实例对象f、method.setAccessible(true)与method.invoke(obj,args)——取消访问权限检查与调用对应方法g、field.setInt(obj,6)——设置对象的属性值为6代理模式静态代理代理类在编译时就实现好是一个实际的class文件、动态代理代理类在运行时动态生成的类字节码并加载到JVM中动态代理主要涉及两个类Proxy、InvocationHandler都在java.lang.reflect包下内部主要通过反射实现Proxy这是生成代理类的主类通过Proxy类生成的代理类都继承自Proxy类是所有动态代理类的父类并且提供了用户创建动态代理类静态方法getProxyClass和创建代理对象静态方法newProxyInstanceInvocationHandler调用动态代理类中方法时将会直接转接到执行自定义InvocationHandler中的invoke()方法
注解
元数据是关于数据的数据对数据进行说明描述添加到程序元素如方法、字段、类和包上的额外信息注解Annotation就是Java平台的元数据允许在Java代码中添加自定义注释并允许通过反射以编程方式访问元数据常见注解Override(可适用元素为方法仅仅保留在Java源文件中)、Deprecated、SuppressWarnings元注解Documented被提取成文档、Inherited某个类定义Xxx注解时使用Inherited修饰那么其子类自动被该Xxx注解修饰、Retention(SOURCE、CLASS、RUNTIME)注解保留时长、Target(CONSTRUCTOR、FIELD、METHOD等)注解所适用的程序元素类型注解保留策略为RetentionPolicy.RUNTIME时才可在运行期通过反射机制来使用
IO流
输入输出流输入流只能从中读取数据而不能向其写入数据、输出流只能向其写入数据而不能从中读取数据字节流与字符流字符流的由来——因为数据编码不同而有了对字符进行高效操作的流对象本质其实就是基于字节流读取时去查了指定的码表。处理纯文本数据优先考虑使用字符流除此之外都使用字节流节点流与处理流节点流FileInputStream低级流可以从/向一个特定IO设备(如磁盘、网络)读/写数据的流、处理流BufferedInputStream高级流包装节点流通过封装后的流来实现数据读/写功能IO流分类分为如下4种抽象基类InputStream输入字节流、OutputStream输出字节流、Reader输入字符流、Writer输出字符流IO流关闭在执行完输入流操作后要调用close()方法来关闭因为程序里打开的IO资源不属于内存资源垃圾回收机制无法回收该资源所以应该显式关闭文件IO资源在执行完输出流操作后要调用close()方法来关闭关闭输出流除了可以保证流的物理资源被回收之外还能将输出流缓冲区数据flush到物理节点里(因为在执行 close()方法之前会自动执行输出流的flush()方法RandomAccessFile优缺点和使用场景优点既可以读取文件内容也可以向文件输出数据同时支持自由访问文件任意位置(内部包含一个记录指针来实现随机访问)、缺点只能读写文件不能读写其他IO节点、使用场景网络请求中多线程下载及断点续传NIONew IO新型IO可替代标准IO并提供与标准IO不同工作方式标准IO基于字节流和字符流进行操作而NIO基于通道(Channel)和缓冲区(Buffer)进行操作数据总是从通道读到缓冲区中或从缓冲区写入通道
异常、深浅拷贝与Java8新特性
Java异常
是什么是Java提供的一种识别及响应错误的一致性机制Java异常机制可以使程序中异常处理代码和正常业务代码分离保证程序代码更加优雅并提高程序健壮性关键字try用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内当 try语句块内发生异常时异常就被抛出、catch用于捕获异常。catch用来捕获try语句块中发生的异常、finally总是会被执行。用于回收在try块里打开的资源(如数据库连接、网络连接和磁盘文件)、throw用于抛出异常、throws用在方法签名中用于声明该方法可能抛出的异常finally与return执行顺序a、只有finally块执行完成之后才会回来执行try或catch块中的return或throw语句(finally语句在return语句执行之后return返回之前执行)b、如果finally中使用了return或者throw等终止方法的语句则就不会跳回执行直接停止c、如果finally语句中没有return语句覆盖返回值那么原来的返回值可能因为finally里的修改而改变(改变引用)也可能不变(改变值)异常框架a、Throwable是Java 语言中所有错误或异常的超类b、Throwable包含Error和Exception两个子类c、Exception类本身以及它的子类中除了运行时异常之外的其它子类都属于被检查异常d、RuntimeException是Exception子类编译器不会检查运行时异常(没有通过throws声明抛出RuntimeException异 常也没有通过try...catch...处理该异常也能通过编译)e、Error用于指示合理的应用程序不应该试图捕获的严重问题和RuntimeException一样编译器也不会检查Error
深浅拷贝
Java中有三种类型对象拷贝浅拷贝(Shallow Copy)、深拷贝 (Deep Copy)、延迟拷贝(Lazy Copy)浅拷贝按位拷贝对象会创建一个新对象这个对象有着原始对象属性值的一份精确拷贝如果属性是基本类型拷贝的就是基本类型的值如果属性是引用类型拷贝的就是内存地址 因此如果其中一个对象改变了这个地址 就会影响到另一个对象、实现方式需要拷贝的类实现Clonable接口并重写clone()方法并在方法中调用super.clone()深拷贝会拷贝所有的属性并拷贝属性指向的动态分配内存当对象和它所引用的对象一起拷贝时即发生深拷贝深拷贝相比于浅拷贝速度较慢并且花销较大、实现方式a、new实现需要拷贝的类实现Clonable接口并重写clone()方法并在方法中new一个对象(类中有引用对象才会发生深拷贝)b、序列化实现延迟拷贝是浅拷贝和深拷贝的一个组合从外面看起来就是深拷贝但是只要有可能它就会利用浅拷贝的速度当原始对象中的引用不经常改变的时候可以使用延迟拷贝深浅拷贝选择a、如果对象属性全是基本类型、对象引用任何时候都不会被改变可以使用浅拷贝b、如果对象有引用属性就要基于具体需求来选择浅拷贝还是深拷贝如果对象引用经常改变就要使用深拷贝
Java8新特性
Lambda表达式和函数式接口函数接口指只有一个函数的接口这样的接口可以隐式转换为Lambda表达式只要某个开发者在该接口中添加一个函数则该接口就不再是函数式接口进而导致编译失败为了克服这种代码层面的脆弱性并显式说明某个接口是函数式接口Java8 提供了一个特殊的注解FunctionalInterface(Java库中的所有相关接口都已经带有这个注解)接口新增特性除了常量和抽象方法在Java8增加了默认方法和静态方法(在Java9增加了私有方法)Java8抽象类和接口区别a、继承抽象类只能单继承接口可以被接口多重继承、被类多实现b、设计理念不同抽象类表示的是is-a关系(这个对象是什么)接口表示的是like-a关系(这个对象能做什么)c、变量接口中定义的变量默认是public static final型且必须给其初值实现类中不能重新定义也不能改变其值抽象类中的变量是普通变量其值可以在子类中重新定义也可以重新赋值
并发
线程
创建线程的4种方式实现Runnable接口实现run方法调用时作为参数传递给Thread构造方法、继承Thread类实现run方法、使用Java8的Lambdanew Thread(()- System.out.println(Thread.currentThread().getName())).start()、使用Callable和FutureCallable和Runnable类似JDK1.5提供以弥补调用线程没有返回值的情况join方法线程加入描述可以使线程之间并行执行变为串行执行比如a、在线程a中调用线程b.join()方法那么线程a就会进入阻塞状态直到线程b执行完以后线程a才会结束阻塞状态也叫等待b线程死亡b、如果线程a中调用线程b.join(10)那么a线程会等待b线程执行10毫秒10毫秒过后a和b线程并行执行、原理join方法通过Object.wait方法实现。当a线程中调用b.join时a线程会获得线程b的锁(wait意味着拿到该对象的锁)调用线程b对象的wait(等待时间)直到该线程b对象唤醒a线程(也就是线程b执行完毕退出时)、注意由于join方法中会抛出异常所以还需要try-catch来捕捉处理异常interrupt方法线程中断interrupt作用其实也不是中断线程而是通知线程应该中断一个线程不应该由其他线程来强制中断或停止而是应该由线程自己自行停止 yield方法线程让步让掉当前线程CPU执行的时间片让当前线程或其它线程运行是让自己或其他线程运行并不是单纯让给其他线程sleep方法线程睡眠让当前线程睡眠指定毫秒在指定时间内当前线程处于阻塞状态sleep()和wait()区别区别sleep为线程方法而wait为Object方法他们功能相似、本质区别sleep不释放锁wait释放锁、用法上不同sleep(milliseconds)可以用时间指定来使他自动醒过来如果时间不到你只能调用interreput()来终止线程wait()可以用notify()/notifyAll()直接唤起线程同步一共synchronized(代码块同步、方法同步)和ReentrantLock(ReentrantLock lock new ReentrantLock(); lock.lock();lock.unlock())两种锁来实现线程同步问题乐观锁ReentrantLock悲观锁synchronzed、Lock
线程池
线程池优势a、降低系统资源消耗通过重用已存在线程降低线程创建和销毁造成的消耗b、提高系统响应速度当有任务到达时无需等待新线程创建便能立即执行c、方便线程并发管控线程若是无限制创建不仅会额外消耗大量系统资源更是占用过多资源而阻塞系统或oom等状况从而降低系统稳定性而线程池能有效管控线程统一分配、调优提高资源使用率d、更强大的功能线程池提供了定时、定期以及可控线程数等功能的线程池使用方便简单ThreadPoolExecutor参数corePoolSize核心线程数默认情况下核心线程一直存活在线程池中即便处于闲置状态除非将ThreadPoolExecutor的allowCoreThreadTimeOut属性设为true这时候处于闲置的核心线程在等待新任务到来时会有超时策略一旦超过keepAliveTime所设置的超时时间闲置核心线程就会被终止、maximumPoolSize最大线程数包含核心线程数非核心线程数活动线程达到这个数以后后续新任务将会被阻塞、keepAliveTime非核心线程闲置时的超时时长对于非核心线程闲置时间超过这个时间非核心线程就会被回收当allowCoreThreadTimeOut属性设为true时这个超时时间才会对核心线程产生效果、unit用于指定keepAliveTime参数的时间单位、workQueue保存等待执行任务的阻塞队列通过线程池execute方法提交的Runable对象都会存储在该队列中、threadFactory线程工厂为线程池提供新线程的创建、handler是RejectedExecutionHandler对象当任务队列已满且线程池中活动线程已达到限定最大值或是无法成功执行任务这时ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution方法线程池执行流程提交任务——核心线程数是否已满(未满就创建核心线程执行任务)——任务队列是否已满(未满就加入队列)——线程池是否已满(未满就创建非核心线程执行任务)——拒绝执行(调用handler.rejectedExecution通知调用者)newFixedThreadPool()内部ThreadPoolExecutor实现new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue())——只有核心线程并且这些线程都不会被回收不存在超时机制采用LinkedBlockingQueue在于其无界性(对于任务队列大小也没有限制)newCachedThreadPool()(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS, new SynchronousQueue()) ——没有核心线程最大线程数为整数最大值闲置60s的线程会被回收采用SynchronousQueue(内部是个空集合任务队列大小为0如果现有线程无法接收任务将会创建新线程来执行任务)newScheduledThreadPool()(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue())——核心线程数固定最大线程数为整数最大值非核心线程处于限制状态时会立即被回收创建一个可定时执行或周期执行任务的线程池返回值是ScheduledExecutorServiceScheduledExecutorServiceschedule方法延迟一定时间后执行任务、scheduleAtFixedRate延迟一定时间后以一定间隔周期性执行任务时间间隔是从上一个任务开始执行到下一个任务开始执行的间隔这一系列任务的触发时间都是可预知、scheduleWithFixedDelay时间间隔是从上一个任务执行结束到下一个任务开始执行的间隔这一系列任务的触发时间不可预知newSingleThreadExecutor()new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue())线程池使用CPU密集型任务线程池中线程个数应尽量少如配置N1个线程的线程池、IO密集型任务IO操作速度远低于CPU速度CPU绝大多数时间处于空闲状态那么线程池可以配置尽量多线程以提高CPU利用率如2*N、混合型任务拆分成CPU密集和IO密集当这两类任务执行时间相差无几时通过拆分再执行吞吐率高于串行执行吞吐率但若这两类任务执行时间有数据级差距那么没有拆分意义
锁
死锁产生条件互斥条件一个资源每次只能被一个线程使用、请求与保持条件一个线程因请求资源而阻塞时对已获得的资源保持不放、不剥夺条件线程已获得的资源在未使用完之前不能强行剥夺、循环等待条件若干线程之间形成一种头尾相接的循环等待资源关系死锁分类和解决办法静态的锁顺序死锁所有需要多个锁的线程都要以相同顺序来获得锁、动态的锁顺序死锁使用System.identifyHashCode来定义锁的顺序确保所有线程都以相同顺序获得锁、协作对象之间发生的死锁需要使用开放调用(开放调用就是调用某个外部方法时不需要持有锁)避免在持有锁的情况下调用外部方法重入锁当一个线程得到一个对象后再次请求该对象锁时是可以再次得到该对象锁的也就是说自己可以再次获取自己的内部锁Java内置锁(synchronized)和Lock(ReentrantLock)都是可重入的公平锁优先级低的线程可能一直无法获取到CPU执行权公平锁就可以保证线程按照时间先后顺序执行避免产生饥饿现象但公平锁效率比较低因为需要维护一个有序队列以实现顺序执行ReentrantLock便是一种公平锁通过在构造方法中传入true就是公平锁传入false就是非公平锁默认falsesynchronized和Lock的区别a、Lock是一个接口而synchronized是Java中的关键字synchronized是内置的语言实现b、synchronized在发生异常时会自动释放线程占有的锁因此不会导致死锁现象发生而Lock在发生异常时如果没有主动通过unLock()去释放锁则很可能造成死锁现象因此使用Lock时需要在finally块中释放锁c、Lock可以让等待锁的线程响应中断而synchronized却不行使用synchronized时等待的线程会一直等待下去不能够响应中断d、通过Lock可以知道有没有成功获取锁而synchronized却无法办到e、Lock可以提高多个线程进行读操作的效率
volatile
原子性一个或多个操作要么全部执行且执行过程不会被任何因素打断要么都不执行可见性当多个线程访问同一个变量时一个线程修改了这个变量的值其他线程能够立即看到修改后的值有序性程序执行的顺序按照代码的先后顺序执行指令重排序处理器为了提高程序运行效率可能会对输入代码进行优化它不保证程序中各个语句执行先后顺序同代码中顺序一致但它会保证程序最终执行结果和代码顺序执行结果是一致的指令重排序不会影响单个线程的执行但是会影响到线程并发执行的正确性锁与原子性、可见行、有序性a、Java内存模型只保证基本读取和赋值是原子性操作如果要实现更大范围操作的原子性可以通过synchronized和Lock来实现b、通过synchronized和Lock也能够保证可见性同一时刻只有一个线程获取锁然后执行同步代码并且在释放锁之前会将对变量的修改刷新到主存当中c、synchronized和Lock同样能保证有序性volatile与原子性、可见性、有序性a、volatile不能确保对变量任何操作都是原子性的而且自增操作不是原子性操作Java5带来的atomic系列如AtomicInteger利用CAS(Compare And Swap)能保证操作的原子性b、volatile保证可见性当一个共享变量被volatile修饰时它会保证修改的值会立即被更新到主存当有其他线程需要读取时它的缓存会无效就会去内存中读取新值c、volatile可以保证有序性禁止指令进行重排序一个方法体中volatile变量所在语句的前/后面语句执行顺序不会变但前/后面语句的内部多条语句执行顺序不做保证volatile应用场景基于多线程下a、作为状态开关b、单例模式中的double check(第一次判空为了效率避免加锁第二次判空为了并发安全volatile避免new语句的指令重排序操作)
JVM
内存区域
方法区(公有)存储已被虚拟机加载的类信息、常量、静态常量、即时编译器编译后代码等数据包含常量池用户存放编译器生成的各种字面量和符号引用异常状态OutOfMemoryError堆(公有)是JVM所管理内存中最大的一块唯一目的就是存放实例对象几乎所有的对象实例都在这里分配Java堆是垃圾收集器管理的主要区域很多时候也被称为“GC堆”异常状态OutOfMemoryError虚拟机栈(线程私有)描述的是Java方法执行的内存模型每个方法在执行时都会创建一个栈帧用户存储局部变量表操作数栈动态连接方法出口等信息每个方法从调用直至完成的过程就对应着一个栈帧在虚拟机栈中入栈到出栈的过程对这个区域定义了两种异常状态OutOfMemoryError、StackOverflowError本地方法栈(线程私有)与虚拟机栈所发挥的作用相似区别不过是虚拟机栈为虚拟机执行Java方法而本地方法栈为虚拟机使用到Native方法服务程序计数器(线程私有)一块较小的内存当前线程所执行字节码的行号指示器字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令
内存模型
Java内存模型目的屏蔽各种硬件和操作系统的内存访问差异以实现让Java程序在各种平台下都能达到一致的内存访问效果主要目标定义程序中各个变量访问规则即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节(此处的变量与Java变成中所说的变量有所区别它包括了实例字段静态字段和构成数组对象的元素但不包括局部变量和方法参数)主内存与线程工作内存的变量Java内存模型规定所有变量都存储在主内存中每条线程在自己工作内存中保存被该线程所使用到的变量(这些变量是从主内存中拷贝而来)线程对变量所有操作(读取、赋值)都必须在工作内存中进行不同线程之间也无法直接访问对方工作内存中变量线程间变量值的传递均需要通过主内存来完成
类加载机制
定义把class文件加载到内存并对其进行验证、准备、解析和初始化最终形成可被虚拟机直接使用的Java类型类的生命周期加载、连接(验证准备解析)、初始化、使用和卸载触发类加载时机newstatic字段static方法反射初始化类时先初始化父类类加载具体过程加载a、通过全限定类名类加载器获取类的二进制字节流b、将这个字节流所代表的静态存储结构转换为方法区内的运行时数据结构c、在内存中生成代表这个类的Class对象作为方法区这个类的各种数据访问入口、验证Class文件格式、元数据验证、字节码验证、符号引用验证、准备在方法区为类变量分配内存并设置初始值、解析虚拟机将常量池内的符号引用替换为直接引用的过程动态和静态解析、初始化执行类构造器(与类的构造函数不同不需要显示调用父类构造器虚拟机会保证在子类方法执行之前父类构造器已经执行完毕)、static代码块、给类变量赋值类加载器分类主要分为两类启动类加载器Bootstrap ClassLoaderC语言实现(针对HotSpot)负责加载Java核心类将存放在\lib目录或-Xbootclasspath参数指定路径中的类库加载到内存中、其他类加载器由Java语言实现继承自抽象类ClassLoader。扩展类加载器——Extension ClassLoaderJava语言实现负责加载Java扩展核心类之外的类加载\lib\ext目录或java.ext.dirs系统变量指定路径中的所有类库应用程序类加载器——Application ClassLoaderJava语言实现默认加载器负责加载用户类路径classpath上指定类库通过 ClassLoader.getSystemClassLoader()方法直接获取Android类加载器Android中最主要类加载器有如下4个BootClassLoader加载Android Framework层中的class字节码文件类似java的Bootstrap ClassLoader但通过Java实现代码在ClassLoader文件内部、PathClassLoader加载已经安装到系统中Apk的class字节码文件(类似Java的App ClassLoader)、DexClassLoader加载制定目录的class字节码文件(类似Java中的Custom ClassLoader)、BaseDexClassLoaderPathClassLoader和DexClassLoader的父类双亲委派模型自定义类加载器——Application ClassLoader——Extension ClassLoader——Bootstrap ClassLoader双亲委派模型代码实现ClassLoader中loadClass方法实现了双亲委派模型自定义类加载器就主要重写findClass方法
垃圾回收机制
垃圾收集算法标记-清除算法标记和清除效率都不高、产生大量内存碎片、复制算法为了解决效率问题内存划分为大小相等两块分配效率高但内存缩小一半、标记-整理算法对象存活率高时复制算法效率变低标记过程仍然与”标记-清除“算法一样但后续步骤不是直接对可回收对象进行清理而是让所有存活对象都向一端移动然后直接清理掉边界以外的内存、分代收集算法新生代使用复制算法、老年代采用标记清除或标记整理进行Java堆内存回收JVM中的年代新生代1个Eden区存放新建对象和2个Survivor区(From Survivor区与To Survivor区)保存还存活对象默认内存划分比例为8:1Minor GC时“Eden”存活对象被复制到“To”“From”区年龄未达到阈值的对象被复制到“To”区然后“From”和“To”会交换角色保证名为To的Survivor区域是空的、老年代“From”区年龄达到阈值的对象会被移动到老年代中“To”区被填满之后会将所有对象移动到老年代中Minor GC和Full/Major GCMinor GC发生在新生代的垃圾收集动作非常频繁Major、Full/Major GC发生在老年代Major GC通常会伴随至少一次有Minor GC且Major GC速度比Minor GC慢10倍以上空间分配担保在发生Minor GC之前虚拟机会先检查老年代最大可用连续空间是否大于新生代所有对象总空间大于那么可以确保Minor GC是安全的小于则可能进行Full GC
如何判断对象已死
引用计数法给对象添加一个引用计数器当多一个引用则计数器就加1引用失效则计数器减1计数器为0表示对象不再使用JVM未使用这种方式因为很难解决对象间互循环引用的问题可达性分析算法GC Roots对象Java栈对象、Native栈对象、方法区static对象和final对象、算法描述通过一些列GC Roots对象作为起始点从这些节点开始向下搜索所走过的路径称为引用链当一个对象到GC Roots没有任何引用链相连时(就是从GC Roots到这个对象是不可达)则证明此对象是不可用的它们会被判定为可回收对象、宣告对象死亡的两次标记过程对象没有与GC Roots相连接会被第一次标记并进行一次筛选(此对象是否有必要执行finalize方法)如果对象在finalie()中重新与引用链上任何一个对象建立关联那在第二次标记时它将会被移除出“即将回收”集合但如果没建立联系就会被回收对象是否存活与引用相关4大引用强引用OOM也不会回收对象、软引用内存不足时(OOM之前)回收如图片缓存、弱引用下一次GC时回收防止内存泄漏、虚引用随时可能被回收为一个对象设置虚引用的唯一目的就是能在这个对象被GC回收时能得到一个系统通知Java
集合
集合概述
HashMap
ConcurrentHashMap
泛型
反射
注解
IO流
异常、深浅拷贝与Java8新特性
Java异常
深浅拷贝
Java8新特性
并发
线程
线程池
锁
volatile
JVM
内存区域
内存模型
类加载机制
垃圾回收机制
如何判断对象已死