26. String, StringBuilder,StringBuffer
String 是 Java 中底子且紧张的类,被声明为 final class(StringBuilder,StringBufffer都是final类,不可继续),是不可变字符串。因为它的不可变性,所以拼接字符串时间会产生很多无用的中间对象,如果频繁的进行这样的操作对性能有所影响。
StringBuffer 就是为了解决大量拼接字符串时产生很多中间对象问题而提供的一个类。它提供了 append 和 add 方法,可以将字符串添加到已有序列的末端或指定位置,它的本质是一个线程安全的可修改的字符序列。
在很多情况下我们的字符串拼接操作不须要线程安全,所以 StringBuilder 登场了。StringBuilder 是 JDK1.5 发布的,它和 StringBuffer 本质上没什么区别,就是去掉了包管线程安全的那部分,减少了开销。
StringBuffer线程安全, StringBuilder线程不安全。一般情况下,速率从快到慢为 StringBuilder > StringBuffer > String,当然这是相对的,不是绝对的。
使用环境:操作少量的数据使用 String。单线程操作大量数据使用 StringBuilder。多线程操作大量数据使用 StringBuffer。
27. 把一段逗号分割的字符串转换成一个数组?
1)用正则表达式,代码大概为:String [] result = orgStr.split(“,”);
2)用 StingTokenizer:StringTokenizer tokener = StringTokenizer(orgStr,”,”);
String [] result = new String[tokener .countTokens()];
Int i=0;
while(tokener.hasNext(){result[i++]=toker.nextToken()
28.程序代码输出的效果是多少?
public class smallT{
public static void main(String args[]){
smallT t = new smallT(); int b = t.get(); System.out.println(b); }
public int get() { try { return 1; } finally{ return 2 ; } } }
效果是2。try中的return语句调用的函数先于finally中调用的函数实验,也就是说return语句先实验,finally语句后实验,所以,返回的效果是2。Return并不是让函数马上返回,而是return语句实验后,将把返回效果放置进函数栈中,此时函数并不是马上返回,它要实验finally语句后才真正开始返回. 结论:finally中的代码比return 和break语句后实验
29. final, finally, finalize的区别?
final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继续.内部类要访问局部变量,局部变量必须定义成final范例.
finally是异常处置惩罚语句结构的一部分,表示总是实验。
finalize是Object类的一个方法,在垃圾网络器实验的时间会调用被回收对象的此方法,可以覆盖此方法提供垃圾网络时的其他资源回收,比方关闭文件等。JVM不包管此方法总被调用.
30.Java中的异常处置惩罚机制的简单原理和应用?
异常是指java程序运行时(非编译)所发生的非正常情况或错误,Java使用面向对象的方式来处置惩罚异常,它把程序中发生的每个异常也都分别封装到一个对象来表示的,该对象中包含有异常的信息。
Java对异常进行了分类,所有异常的根类为java.lang.Throwable,Throwable下面又派生了两个子类:Error和Exception,Error 表示应用程序本身无法降服和恢复的一种严重问题,程序只有死的份了,比方,说内存溢出和线程死锁等体系问题。Exception表示程序还可以或许降服和恢复的问题,表示一种设计或实现问题。它表示如果程序运行正常,从不会发生的情况。此中又分为体系异常和平凡异常,体系异常是软件本身缺陷所导致的问题,也就是软件开发职员思量不周所导致的问题,软件使用者无法降服和恢复这种问题,但在这种问题下还可以让软件体系继续运行或者让软件死掉,比方,数组脚本越界(ArrayIndexOutOfBoundsException),空指针异常(NullPointerException)、类转换异常(ClassCastException);平凡异常是运行环境的变革或异常所导致的问题,是用户可以或许降服的问题,比方,网络断线,硬盘空间不敷,发生这样的异常后,程序不应该死掉。
java为体系异常和平凡异常提供了差别的解决方案,编译器强制平凡异常必须try..catch处置惩罚或用throws声明继续抛给上层调用方法处置惩罚,所以平凡异常也称为checked异常,而体系异常可以处置惩罚也可以不处置惩罚,所以,编译器不强制用try..catch处置惩罚或用throws声明,所以体系异常也称为unchecked异常。
31. java中有几种方法实现一个线程?stop()和suspend()方法为何不推荐使用?
一.是实现Runnable接口实现它的run()方法。
二.种是继续Thread类,覆盖它的run()方法。
三.通过Callable和FutureTask创建线程 (通过第1种创建)
四.通过线程池创建线程(通过第2种创建)
注意:无论是 Callable 还是 FutureTask,和 Runnable 一样,都是一个任务,是须要被实验的,而不是说它们本身就是线程。他们可以放到线程池实验,由线程去实验他们,所以本质还是线程去实验,而子线程的创建方式仍脱离不了最开始讲的两种根本方式,也就是实现 Runnable 接口和继续 Thread 类。
反对用stop(),因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。效果很难检查出真正的问题所在。suspend()方法容易发存亡锁。调用suspend()的时间,目标线程会停下来,但却仍旧持有在这之前得到的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该运动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。
32.sleep() 和 wait() 区别
sleep是线程类(Thread)的方法,导致此线程暂停实验指定时间,给实验机会给其他线程,但是监控状态依然保持,到时后会主动恢复。调用sleep不会释放对象锁。 wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池预备得到对象锁进入运行状态.
sleep就是正在实验的线程主动让出cpu,cpu去实验其他线程,在sleep指定的时间过后,cpu才会回到这个线程上继续往下实验,如果当火线程进入了同步锁,sleep方法并不会释放锁,纵然当火线程使用sleep方法让出了cpu,但其他被同步锁挡住了的线程也无法得到实验。wait是指在一个已经进入了同步锁的线程内,让自己暂时让出同步锁,以便其他正在等待此锁的线程可以得到同步锁并运行,只有其他线程调用了notify方法(notify并不释放锁,只是告诉调用过wait方法的线程可以去加入得到锁的竞争了,但不是马上得到锁,因为锁还在别人手里,别人还没释放。如果notify方法后面的代码还有很多,须要这些代码实验完后才会释放锁,可以在notfiy方法后增加一个等待和一些代码,看看效果),调用wait方法的线程就会解除wait状态和程序可以再次得到锁后继续向下运行。
同步的实现方面有两种,分别是synchronized,wait与notify
wait():使一个线程处于等待状态,而且释放所持有的对象的lock。
sleep():使一个正在运行的线程处于就寝状态,是一个静态方法,调用此方法要捕获InterruptedException异常。
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时间,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
33.同步和异步有何异同,在什么情况下分别使用他们?
如果数据将在线程间共享。比方正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。
当应用程序在对象上调用了一个须要耗费很长时间来实验的方法,而且不渴望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有服从。
34.启动一个线程是用run()还是start()?
启动一个线程是调用start()方法,使线程就绪状态,以后可以被调理为运行状态,一个线程必须关联一些详细的实验代码,run()方法是该线程所关联的实验代码
35. 一个线程进入一个对象的一个synchronized方法后,别的线程是否可进入此对象的别的方法?
分几种情况,
1) 其他方法前是否加了synchronized关键字,如果没加,则能。
2)如果这个方法内部调用了wait,则可以进入其他synchronized方法。
3)如果其他个方法都加了synchronized关键字,而且内部没有调用wait,则不能。
4)如果其他方法是static,它用的同步锁是当前类的字节码,与非静态的方法不能同步,因为非静态的方法用的是this
36.线程的根本概念、线程的根本状态以及状态之间的关系?
一个程序中可以有多条实验线索同时实验,一个线程就是程序中的一条实验线索,每个线程上都关联有要实验的代码,即可以有多段程序代码同时运行,每个程序至少都有一个线程,即main方法实验的谁人线程。如果只是一个cpu,它怎么可以或许同时实验多段程序呢?这是从宏观上来看的,cpu一会实验a线索,一会实验b线索,切换时间很快,给人的感觉是a,b在同时实验,好比大家在同一个办公室上网,只有一条链接到外部网线,实在,这条网线一会为a传数据,一会为b传数据,由于切换时间很短暂,所以,大家感觉都在同时上网。
状态: 就绪,运行,synchronize阻塞,wait和sleep挂起,结束。wait必须在synchronized内部调用。
调用线程的start方法后线程进入就绪状态,线程调理体系将就绪状态的线程转为运行状态,遇到synchronized语句时,由运行状态转为阻塞,当synchronized得到锁后,由阻塞转为运行,在这种情况可以调用wait方法转为挂起状态,当线程关联的代码实验完后,线程变为结束状态。
37.synchronized和java.util.concurrent.locks.Lock的异同?
特性区别: synchronized是Java内置的一个线程同步关键字,而Lock是J.U.C包下面的一个接口,它有很多实现类,好比ReentrantLock就是它的一个实现类.
用法区别:synchronized可以写在须要同步的对象、方法或者是特定代的码块中。主要有两种写法,好比这样:
一种是把synchronized修饰在方法上 public synchronized void sync(){}
一种是把synchronized修饰在代码块上
Object lock = new Object(); public void sync(){ synchronized(lock){} }
用这种方式来控制锁的生命周期。而Lock控制锁的粒度是通过lock() 和 unlock() 方法来实现的,以ReentrantLock为例,看代码:
Lock lock = new ReentrantLock(); public void sync(){
lock.lock();//加锁
// TODO 线程安全的代码
lock.nulock();//释放} 这种方式,是可以包管lock()方法和unlock()方法之间的代码是线程安全的。而锁的作用域,取决于Lock实例的生命周期。
Lock比synchronized在使用上相对来说要更加灵活一些。Lock可以自主地去决定什么时间加锁,什么时间释放锁。只须要调用lock()和unlock()这两个方法就可以了。须要注意的是,为了避免死锁,一般我们unlock()方法写在finally块中。别的,Lock还提供了非阻塞的竞争锁的方法叫trylock(),这个方法可以通过返回true或者fasle来告诉当火线程是否已经有其他线程正在使用锁。
而synchronized是关键字,无法去扩展实现非阻塞竞争锁的方法。别的,synchronized只有代码块实验结束或者代码出现异常的时间才会释放锁,因此,它对锁的释放是被动的。
性能区别:synchronized和Lock在性能上差别不大。在实现上有一些区别,synchronized 采用的是悲观锁机制,synchronized 是托管给 JVM 实验的。在JDK1.6以后采用了偏向锁、轻量级锁、重量级锁及锁升级的方式进行优化。
而 Lock 用的是乐观锁机制。控制锁的代码由用于自定义,也采用CAS自旋锁进行了优化。
用途区别:二者在一般情况下没有什么区别,但是在非常复杂的同步应用中,建议使用Lock。因为synchronized只提供了非公平锁的实现,而Lock提供了公平所和非公平锁的机制。
公平锁是指线程竞争锁资源的时间,如果已经有其他线程正在排队或者等待锁释放,那么当前竞争锁的线程是无法去插队的。
而非公平锁就是不管是否线程再排队等待锁,它都会去尝试竞争一次锁。
38.两个对象值类似(x.equals(y) == true),但却可有差别的hash code,对不对?
对。如果对象要保存在HashSet或HashMap中,它们的equals相称,那么,它们的hashcode值就必须相称。如果不是要保存在HashSet或HashMap,则与hashcode没有什么关系了,这时间hashcode不等是可以的,比方arrayList存储的对象就不消实现hashcode,当然,我们没有来由不实现,通常都会去实现的。
39. heap和stack区别?
Java的内存分为两类,一类是栈内存,一类是堆内存。栈内存是指程序进入一个方法时,会为这个方法单独分配一块私属存储空间,用于存储这个方法内部的局部变量,当这个方法结束时,分配给这个方法的栈会释放,这个栈中的变量也将随之释放。堆是与栈作用差别的内存,一般用于存放不放在当前方法栈中的那些数据,比方,使用new创建的对象都放在堆里,所以,它不会随方法的结束而消散。方法中的局部变量使用final修饰后,放在堆中,而不是栈中.
40. 能不能自己写个类叫java.lang.String?
可以,但在应用的时间,须要用自己的类加载器去加载,否则,体系的类加载器永远只是去加载jre.jar包中的谁人java.lang.String。由于在tomcat的web应用程序中,都是由webapp自己的类加载器先自己加载WEB-INF/classess目录中的类,然后才委托上级的类加载器加载,如果我们在tomcat的web应用程序中写一个java.lang.String,这时间Servlet程序加载的就是我们自己写的java.lang.String,但是这么干就会出很多潜伏的问题,原来所有用了java.lang.String类的都将出现问题。
虽然java提供了endorsed技术,可以覆盖jdk中的某些类,但是,可以或许被覆盖的类是有限制范围,反正不包括java.lang这样的包中的类.
41. java事务范例
1) JDBC 事务, 是用 Connection 对象控制的。JDBC Connection 接口( java.sql.Connection )提供了两种事务模式:主动提交和手工提交。使用 JDBC 事务界定时,您可以将多个SQL语句结合到一个事务中。JDBC事务的一个缺点是事务的范围局限于一个数据库毗连。一个 JDBC 事务不能超过多个数据库。
2) JTA(Java Transaction API)事务;@Transactional注解
3)容器事务
42. jdk署理
动态署理是程序在运行过程中主动创建一个署理对象代替被署理的对象去实验相应的操作。在Java的动态署理机制中有两个紧张的类或接口,一个是InvocationHandler接口,另一个是Proxy类,这两个是实现动态署理必须用到的。
对象A通过持有另一个对象B,可以具有B同样的行为的模式。为了对外开放协议,B往往实现了一个接口,A也会去实现接口。但是B是“真正”实现类,A则比较“虚”,他借用了B的方法去实现接口的方法。A虽然是“伪军”,但它可以加强B,在调用B的方法前后都做些其他的事变。Spring AOP就是使用了动态署理完成了代码的动态“织入”。
使用署理利益还不止这些,一个工程如果依赖另一个工程给的接口,但是另一个工程的接口不稳固,经常变更协议,就可以使用一个署理,接口变更时,只须要修改署理,不须要逐一修改业务代码。从这个意义上说,所有调外界的接口,我们都可以这么做,不让外界的代码对我们的代码有侵入,这叫防御式编程。署理其他的应用可能还有很多。 上例中,类A写死持有B,就是B的静态署理。如果A署理的对象是不确定的,就是动态署理。动态署理现在有两种常见的实现,jdk动态署理(jre提供的类库直接使用)和cglib动态署理。简单说,动态署理就是要生成一个包装类对象,由于署理的对象是动态的,所以叫动态署理。由于我们须要加强,这个加强是须要留给开发职员开发代码的,因此署理类不能直接包含被署理对象,而是一个InvocationHandler,该InvocationHandler包含被署理对象,并负责分发哀求给被署理对象,分发前后均可以做加强。从原理可以看出,JDK动态署理是“对象”的署理。动态署理有一个缺点,那就是只能署理基于接口的类,而无法署理没有接口的委托类.
静态署理缺点, 1.署理对象的一个接口只服务于一种范例的对象,如果要署理的方法很多,势须要为每一种方法都进行署理,静态署理在程序规模稍大时就无法胜任了, 2.如果接口增加一个方法,除了所有实现类须要实现这个方法外,所有署理类也须要实现此方法。增加了代码维护的复杂度。
动态署理长处:与静态相比最大的利益是接口中声明的所有方法都被转移到调用处置惩罚器一个会合的方法中处置惩罚(InvocationHandler.invoke)。这样,在接口方法数目较多的时间,我们可以进行灵活处置惩罚,而不须要像静态署理那样每一个方法进行中转。而且动态署理的应用使我们的类职责更加单一,复用性更强。
43. Java8新特性
接口的默认方法, Java 8答应我们给接口添加一个非抽象的方法实现,只须要使用 default关键字即可,这个特性又叫做扩展方法.
1)Lambda创建流stream()−为聚集创建串行流。parallelStream()−为聚集创建并行流,parallelStream()方法可以或许充分利用多核CPU的优势,使用多线程加速对聚集数据的处置惩罚速率。加入并行处置惩罚的线程有主线程以及ForkJoinPool中的worker线程。parallelStream底层是基于ForkJoinPool,ForkJoinPool实现了ExecutorService接口,Fork/Join框架主要采用分而治之的理念来处置惩罚问题,对于一个比较大的任务,首先将它拆分(fork)为两个小任务task1与task2。使用新的线程thread1去处置惩罚task1,thread2去处置惩罚task2。如果thread1认为task1太大,则继续往下拆分成新的子任务task3与task4。thread2认为task2任务量不大,则立即进行处置惩罚,形成效果result2。之后将task3和task4的处置惩罚效果归并(join)成result1,最后将result1与result2归并成最后的效果。
2) zStream 提供了新的方法forEach来迭代流中的每个数据。以下代码片段使用 forEach 输出了10个随机数:
Random random = new Random(); random.ints().limit(10).forEach(System.out::println);
3) map方法用于映射每个元素到对应的效果,以下代码片段使用 map 输出了元素对应的平方数:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
List<Integer> sqList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
4) filter,filter方法用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤出空字符串: List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 获取空字符串的数目
int count = strings.stream().filter(string -> string.isEmpty()).count();
limit,limit方法用于获取指定数目的流。以下代码片段使用limit方法打印出10条数据:
Random random = new Random(); random.ints().limit(10).forEach(System.out::println);
5) sorted,sorted方法用于对流进行排序。使用sorted方法对输出的10个随机数进行排序:
Random rand= new Random(); rand.ints().limit(10).sorted().forEach(System.out::println);
6)并行(parallel)程序,parallelStream 是流并行处置惩罚程序的代替方法。使用 parallelStream 来输出空字符串的数目:List<String> strings= Arrays.asList("abc","","bc","efg","abcd","","jkl");
//获取空字符串的数目, 可以很容易的在序次运行和并行直接切换。
int count = strings.parallelStream().filter(string -> string.isEmpty()).count();
7) Collectors类实现了很多归约操作,比方将流转换成聚集和聚合元素。Collectors 可用于返回列表或字符串:List<String>strings = Arrays.asList("abc","","bc","efg","abcd","","jkl");
List<String> filtered =
strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println("筛选列表: " + filtered);
String mergedString =
strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("归并字符串: " + mergedString);
8) 统计summaryStatistics, 别的,一些产生统计效果的网络器也非常有用。它们主要用于int、double、long等根本范例上,它们可以用来产生类似如下的统计效果。
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics(); System.out.println("列表中最大的数:"+ stats.getMax()+"列表中最小的数:"+ stats.getMin());
System.out.println("所有数之和 : " + stats.getSum()+ "平均数: " + stats.getAverage());
44. JVM内存模型?分别存放的什么?
分别是:方法区、堆、Java虚拟机栈、本地方法栈、程序计数器
a)方法区主要用来存放类信息、类的静态变量、常量、运行时常量池等,方法区的巨细是可以动态扩展的
b) 堆Heap主要存放的是数组、类的实例对象、字符串常量池等。堆是OOM故障最主要的发源地,它存储着险些所有的实例对象,堆由垃圾网络器主动回收,堆区由各子线程共享使用;通常情况下,它占用的空间是所有内存区域中最大的,但如果无控制地创建大量对象,也容易消耗完所有的空间;堆的内存空间既可以固定巨细,也可运行时动态地调解,通过如下参数设定初始值和最大值,好比 -Xms256M. -Xmx1024M此中-X表示它是JVM运行参数。ms是memorystart的简称 最小堆容量;mx是memory max的简称 最大堆容量。
对于大多数应用来说,Java堆(Heap)是JVM所管理的内存中最大的一块。它是被所有线程共享的一块内存区域,在虚拟机启动时创建。主要用来存放对象实例,所有的对象实例以及数组都要在堆上分配。堆是垃圾网络器管理的主要区域,也被称为“GC堆”,从内存回收的角度来看,堆可以细分为:新生代和老年代;再细致一点可分为:Eden空间、From Survivor空间、To Survivor空间(空间分配比例是8:1:1)。如果在堆中没有内存完成实例分配,而且堆也无法再扩展时,将抛出OutOfMemoryError异常。
c) 虚拟机栈JVM Stacks,JVM栈是线程私有的内存区域。它形貌的是java方法实验的内存模型,每个方法实验的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用直至完成的过程,都对应着一个栈帧从入栈到出栈的过程。每当一个方法实验完成时,该栈帧就会弹出栈帧的元素作为这个方法的返回值,而且清除这个栈帧,Java栈的栈顶的栈帧就是当前正在实验的运动栈,也就是当前正在实验的方法。就像是组成动画的一帧一帧的图片,方法的调用过程也是由栈帧切换来产生效果。
局部变量表存放了编译器可知的各种根本数据范例(int、short、byte、char、double、float、long、boolean)、对象引用(reference范例,它不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)和returnAddress范例(指向了一跳字节码指令的地址)。在JVM规范中,对这个区域规定了两种异常情况:如果线程哀求的栈深度大于虚拟机答应的深度,将抛出StackOverflowError异常;如果虚拟机栈可以动态扩展,在扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常.
d) 本地方法栈 Native Method Stack, 本地方法栈结构上和Java虚拟机栈一样,只不过Java虚拟机栈是运行Java方法的区域,而本地方法栈是运行本地方法的内存模型。
本地方法栈和虚拟机栈所发挥的作用是很相似的,它们之间的区别不过是 虚拟机栈为虚拟机实验Java方法(字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。Sun HotSpot 直接就把本地方法栈和虚拟机栈合二为一。本地方法栈也会抛出StackOverflowError和OutOfMemoryError异常。
e) Program Counter Register (程序计数寄存器),程序计数器是一个比较小的内存空间,用来记载当火线程正在实验的那一条字节码指令的地址。 它可以看作是当火线程所实验的字节码的行号指示器。在虚拟机概念模型里(概念模型,各种虚拟机可能会通过一些更高效的方式实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条须要实验的字节码指令:分支、跳转、循环、异常处置惩罚、线程恢复等底子操作都会依赖这个计数器来完成。每个线程都有独立的程序计数器,用来在线程切换后能恢复到精确的实验位置,各条线程之间的计数器互不影响,独立存储。所以它是一个“线程私有”的内存区域。此内存区域是唯逐一个在JVM规范中没有规定任何OutOfMemoryError情况的区域。
f) Metaspace元空间, 在JDK1.8中,永久代已经不存在,存储的类信息、编译后的代码数据等已经移动到了MetaSpace(元空间)中,元空间并没有处于堆内存上,而是直接占用的本地内存(NativeMemory)。元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。
元空间的巨细仅受本地内存限制,可以通过以下参数来指定元空间巨细:
-XX:MetaspaceSize,初始空间巨细,达到该值就会触发垃圾网络进行范例卸载,同时GC会对该值进行调解:如果释放了大量的空间,就得当降低该值;如果释放了很少的空间,那么在不高出MaxMetaspaceSize时,得当提高该值.
-XX:MaxMetaspaceSize,最大空间,默认是没有限制的.
-XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾网络.
-XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾网络.
45.JVM调优
解决以下问题:1.大哥代年轻代巨细分别是否合理,2.内存走漏, 3.垃圾回收算法设置是否合理.
1)将新对象预留在新生代,降低停顿案例。由于 Full GC 的成本要远远高于 Minor GC ,因此尽可能将对象分配在新生代,在JVM 调优中,可以为应用程序分配一个合理的新生代空间,以最大限度避免新对象直接进去老年代。注意:由于新生代垃圾回收的速率高于老年代回收,因此,将年轻对象预留在新生代有利于提高整体的 GC 服从
2) 大对象进入老年代, 大对象占用空间多,直接放入新生代中会扰乱新生代GC,新生代空间不足将会把大量的较小的年轻代对象移入到老年代中,这对GC来说是相当不利的。如果有短命大对象,对GC来说将会是一场劫难,原本存放于老年代的永久对象,被短命大对象塞满,扰乱了分代内存回收的根本思路,因此,在开发过程中,尽可能避免使用短命的大对象。使用参数 -XX:PretenureSizeThreshold 设置大对象直接进入老年代的阀值,当对象高出这个阀值时,将直接在老年代中分配。此中, -XX:PretenureSizeThreshold 只对串行网络器和新生代并行网络器有用,并行回收网络器不辨认这个参数。注意:短命的大对象对垃圾回收是一场劫难,现在木有一种特殊好的回收方法处置惩罚这个问题,因此尽可能避免使用短命的大对象
3)设置对象进入老年代的年事, 在堆中每个对象都有自己的年事,如果对象在 eden 区,经过一次 GC 后还存活,则被移动到 survivor 区中,对象年事加 1,以后每经过一次 GC 依然存活的,对象年事就加 1。当对象年事达到阀值时,就移动到老年代,这个阀值用以下参数设置:-XX:MaxTenuringThreshold:默认值是15,这个参数是指定进入老年代的最大年事值,对象实际进入老年代的年事是 JVM 在运行时根据内存使用情况动态计算的。如果渴望对象尽可能长地留在新生代中,可以设置一个较大的阀值
4)稳固与震荡的堆巨细, 稳固的堆巨细对垃圾回收是有利的,得到一个稳固堆巨细的方法就是设置 -Xmx 和 -Xms 一样的值。不稳固的堆也不是木有用处,让堆巨细在一个区间内震荡,在体系不须要使用大内存时压缩堆空间,使 GC 应对一个较小的堆,可以加速单次 GC 的速率。基于这种头脑,JVM 提供了两个参数用于压缩和扩展堆空间,参数如下:-XX:MinHeapFreeRatio:设置堆空间最小空闲比例,默认是 40 ,当堆空间的空闲比例小于这个值时,JVM 便会扩展堆空间。-XX:MaxHeapFreeRatio:设置堆空间的最大空闲比例,默认是 70,当堆空间的空闲比例大于这个值时,JVM 便会压缩堆空间,得到一个较小的堆。
注意:当 -Xms 和 -Xmx 相称时,-XX:MinHeapFreeRatio 和 -XX:MaxHeapFreeRatio 这两个参数无效
5) 吞吐量优先设置:机器设置是4G内存和32核CPU,设置参数如下:
-Xms3800m -Xmx3800m(堆的初始值和最大值一样) -Xmn2g(新生代巨细)
-Xss128k:线程栈巨细,减少它使剩余的体系内存支持更多的线程;Xss与线程个数: Xss越大,每个线程的巨细就越大,占用的内存越多,能容纳的线程就越少;Xss越小,则递归的深度越小,容易出现栈溢出 java.lang.StackOverflowError。减少局部变量的声明,可以节省栈帧巨细,增加调用深度。
-XX:+UseParallelGC(新生代使用并行回收网络器)
-XX:ParallelGCThreads=20(垃圾回收的线程数)
-XX:+UseParallelOldGC (老年代使用并行回收网络器)
6)使用非占据的垃圾回收器,为降低应用软件的垃圾回收时的停顿,首先思量的是使用关注体系停顿的 CMS 回收器,其次,为了减少 Full GC 次数,应尽可能将对象预留在年轻代,因为年轻代 Minor GC 的成本远远小于大哥代的 Full GC
–XX arallelGCThreads=20:设置 20 个线程进行垃圾回收;
–XX:+UseParNewGC:年轻代使用并行回收器;
–XX:+UseConcMarkSweepGC:大哥代使用 CMS 网络器降低停顿;
–XX:+SurvivorRatio:设置 Eden 区和 Survivor 区的比例为 8:1。稍大的 Survivor 空间可以提高在年轻代回收生命周期较短的对象的可能性,如果 Survivor 不敷大,一些短命的对象可能直接进入大哥代,这对体系来说是不利的。
–XX:TargetSurvivorRatio=90:设置 Survivor 区的可使用率。这里设置为 90%,则答应 90%的 Survivor 空间被使用。默认值是 50%。故该设置提高了 Survivor 区的使用率。当存放的对象高出这个百分比,则对象会向大哥代压缩。因此,这个选项更有助于将对象留在年轻代。
–XX:MaxTenuringThreshold:设置年轻对象晋升到大哥代的年事。默认值是 15 次,即对象经过 15 次 Minor GC 依然存活,则进入大哥代。这里设置为 31,目标是让对象尽可能地保存在年轻代区域
46.Gc实现原理
堆内存和GC设置毗连,讲解的很好。
https://blog.csdn.net/lilong329329/article/details/82222713#commentBox
JVM分析工具与查看命令: jvm调优成熟的工具已经有很多:jconsole、VisualVM,IBM的Memory Analyzer等等。但Linux环境命令,Sun JDK监控JVM命令有:
•jps JVM Process Status Tool,显示指定体系内所有的HotSpot虚拟机进程;
•jstat 提供Java垃圾回收以及类加载信息;(JVM statistics Monitoring)是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾网络、JIT编译等运行数据。
故障清除命令有
•jcmd 打印一个Java进程的类、线程以及虚拟机信息
•jinfo 访问JVM体系属性,同事可以动态修改这些属性
•jhat 帮助分析内存堆存储
•jmap 提供JVM内存使用信息,(JVM Memory Map)命令用于生成heap dump文件,如果不使用这个命令,还可以使用-XX:+HeapDumpOnOutOfMemoryError参数来让虚拟机出现OOM的时间,主动生成dump文件。jmap不光能生成dump文件,还可以查询finalize实验队列、Java堆和永久代的详细信息,如当前使用率、当前使用的是哪种网络器等。
•jstack Java堆栈跟踪工具
堆快照(堆Dump)在性能问题排查中,分析堆快照是必不可少的一环。获取程序的堆快照文件有多种方法,一种常用的方法,纵然用 -XX:+HeapDumpOnOutOfMemoryError 参数在程序发生 OOM 时,导出应用程序的当前堆快照。这是一种非常有用的方法,因为当程序发生 OOM 退出体系时,一些瞬时信息都随着程序的制止而消散,而重现 OOM 问题往往比较困难或者耗时,因此当发生 OOM 时,将堆信息保存到文件中是至关紧张的,通过下面的参数设置:
-XX:+HeapDumpOnOutOfMemoryError(开启堆快照)
-XX:HeapDumpPath=C:/m.hprof(保存文件到哪个目录)
Java垃圾回收机制,判断一个对象是否该回收: 1.引用计数法,给对象增加一个计数器,当有引用它时,计数器就加一,当引用失效时,计数器就减一; JVM并没有采用这种方式来判断对象是否已死。缘故起因:循环引用会导致引用计数法失效,循环引用就是A类中一个属性引用了B类对象,B类中一个属性引用了A类对象,这样一来,就算你把A类和B类的实例对象引用置为null,它们还是不会被回收;2.可达性分析法,Java则是用了这种方法来判断是否须要回收对象;此算法的核心头脑为:通过一系列称为"GC Roots"的对象作为起始点,从这些节点开始向下搜刮,搜刮走过的路径称之为"引用链",当一个对象到GC Roots没有任何的引用链相连时(从GC Roots到这个对象不可达)时,证明此对象是不可用的;可作为GC Roots的对象有以下几种:
1.虚拟机栈(栈帧中的本地变量表)中引用的对象。2.方法区中类静态属性引用的对象
3.方法区中常量引用的对象。4.本地方法栈中JNI(Native方法)引用的对象
47.Java8为什么要将永久代替换成Metaspace?
1)字符串存在永久代中,容易出现性能问题和内存溢出。
2)类及方法的信息等比较难确定其巨细,因此对于永久代的巨细指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。
3)永久代会为 GC 带来不须要的复杂度,而且回收服从偏低。
4)Oracle 可能会将HotSpot 与 JRockit 合二为一。
48.年轻代参数怎么设置
堆设置 -Xms:初始堆巨细 -Xmx:最大堆巨细 -XX:NewSize=n:设置年轻代巨细
-XX:NewRatio=n:设置年轻代和大哥代的比值。如:为3,表示年轻代与大哥代比值为1:3,年轻代占整个年轻-老代和的1/4
-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5。-XX:MaxPermSize=n:设置持久代巨细
网络器设置:-XX:+UseSerialGC:设置串行网络器 -XX:+UseParallelGC:设置并行网络器
-XX:+UseParalledlOldGC:设置并行大哥代网络器 -XX:+UseConcMarkSweepGC:设置并发网络器
垃圾回收统计信息: -XX:+PrintGC -XX:+PrintGCDetails
-XX:+PrintGCTimeStamps -Xloggc:filename
并行网络器设置
-XX arallelGCThreads=n:设置并行网络器网络时使用的CPU数。并行网络线程数。
-XX:MaxGCPauseMillis=n:设置并行网络最大暂停时间
-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
并发网络器设置
-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
-XX arallelGCThreads=n:设置并发网络器年轻代网络方式为并行网络时,使用的CPU数。并行网络线程数。
49.堆排序
堆是一种数据结构,一种叫做完全二叉树的数据结构。堆的性质:
大顶堆:每个节点的值都大于或者等于它的左右子节点的值。
小顶堆:每个节点的值都小于或者等于它的左右子节点的值。
堆排序根本步调:
1.首先将待排序的数组构造成一个大根堆,此时,整个数组的最大值就是堆结构的顶端
2.将顶端的数与末端的数交换,此时,末端的数为最大值,剩余待排序数组个数为n-1
3.剩余的n-1个数再构造成大根堆,再将顶端数与n-1位置的数交换,反复实验,便能得到有序数组
50.如何包管线程安全
线程安全的实现方法,1)同步代码块;2)同步方法;3)Lock锁机制。通过创建Lock对象,采用lock()加锁,unlock()解锁,来保护指定的代码块。包管线程安全以是否须要同步手段分类,分为同步方案和无需同步方案。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |