秋招准备--java

秋招准备–java


关于java,也不知道现在秋招有多卷,唉

Java工程师

java常量池

java常量池

  • class文件常量池

    存放字面量和符号引用
    
    字面量即文本字符串与final修饰的变量
    
    符号引用则包括
        类和接口的全限定名,也就是如java/lang/String;
        字段(变量)的名称和描述符
        方法中的名称和描述符
        
    描述符即数据类型、方法的参数列表和返回值
    
  • 运行时常量池

    在方法区
    class常量池加载到运行时常量池
    会让class常量池相同的字符串只留下一份
    动态性,即还可以在运行时修改,如String.intern(),不过JDK1.8里应该是去堆里找
    
  • 全局字符串常量池

    JDK1.6及之前在方法区,存的实例
    JDK1.7方法区只存引用,对象在堆中
    JDK1.8在堆中,而且也只存引用
    字面量会在运行时常量池
    本质是个Hash表
    
String s1 = "Hello";
String s2 = "Hello";
String s3 = "Hel" + "lo";
String s4 = "Hel" + new String("lo");
String s5 = new String("Hello");
String s7 = "H";
String s8 = "ello";
String s9 = s7 + s8;

System.out.println(s1 == s2);  // true
System.out.println(s1 == s3);  // true
System.out.println(s1 == s4);  // false
System.out.println(s1 == s5);  // false
System.out.println(s1 == s9);  // false
  • 包装类常量池

    对于5种整型包装对象(包括BOOlean),-128到127对象在常量池里
    

关于Integer

-128到127对象在常量池里

由于Integer的value是final,所以对Integer对象的修改都是新建Integer对象

关于字符串相加

“a”+“b"会被编译成"ab”,而变量相加则会变成new StringBuilder().append(),所以循环中变量拼接建议手动,不然会不断new

还有就是String.contact(),本质是创建一个放得下两个字符串的数组,再把字符串放到数组里,再转化成字符串,在拼接两个字符串快于builder,大量时则慢

反射运用场景

JDBC,XML配置,面向切面

强引用、软引用、弱引用、虚引用

    强引用指向的对象不会被垃圾回收
    软引用指向的对象在内存超限制时,会被回收
    弱引用只要被垃圾回收扫描到就会被回收,由于垃圾回收优先级低,所以会有一定存活时间
    虚引用相当于没有引用,主要用于跟踪对象被垃圾回收的状态,虚引用不能单独使用,必须和引用队列联合使用。
String str=new String("abc");                                     // 强引用
SoftReference<String> softRef=new SoftReference<String>(str);     // 软引用
WeakReference<String> abcWeakRef = new WeakReference<String>(str);// 弱引用
str=null;//有强引用那么软引用求不起效果,同理,另外string不能常量赋值,不然会在常量池里有引用

关于Map

步步深入的解释

包含同步的详细分析

BlockingQueue

多线程下的队列

先进先出,先进后出
分种类有基于数组,基于链表,延时队列(放入一定时间后才能读,维护超时链接),优先队列,无缓冲队列
ArrayBlockingQueue,LinkedBlockingQueue,DelayQueue,PriorityBlockingQueue,SynchronousQueue

Stream

Stream

用类似sql语言的方法处理集合数据

一个流式处理可以分为三个部分:转换成流、中间操作、终端操作。
  
转换操作:把对象转换成流对象,比如调用.stream()串行,parallelStream()并行

中间操作:对数据处理
  过滤:filter、distinct、limit、skip
       filter指定谓词,比如num->num>10,参数为Predicate
       distnct去重,limit限制数量,skip跳过前n个
  排序:sorted,参数为Comparator
  操作:peek可以对每个选出来的数据操作
  映射:map,类似select
       flatMap,扁平化流,即把所有流的每个值转化成流,再合并成一个流

终端操作:实现对流查找、归约等操作
    查找:allMatch,全部满足则返回true;anyMatch只要有一个;noneMatch一个都不满足
        findFirst获得第一个,findAny返回任意一个,对于串行流两者是一样的
    归约:sum求和,foreach对所有元素执行操作
         reduce,比如reduce(初始值,(a,b)->statement),对于并行的还有参数合并器
        collect收集数据,参数为Collectors.toList,toSet,toMap等,
        还可以.groupingBy实现分组和多级分组,partitioningBy分成false和true的两块区域

另外,可以把流转化为迭代器,还有Spliterator支持并行迭代
public class Main {

  public static void main(String[] args) throws IOException {

    class Item {
      int val;

      public Item(int val) {
        this.val = val;
      }
    }
    List<Item> lt = new ArrayList<Item>() {
      {
        add(new Item(0));
        add(new Item(4));
        add(new Item(1));
        add(new Item(3));
        add(new Item(2));
        add(new Item(2));
      }
    };
    
    //下面按照方法调用顺序执行,顺序不同结果不同
    List<Integer> search=lt.stream().filter(item->item.val>=1).distinct().skip(1).limit(2).sorted(Comparator.comparingInt(a -> a.val))
            .map(item -> item.val).collect(Collectors.toList());
    
    int sum = search.stream().reduce(1, (a, b) -> a + b);

    //直接被分成了六个线程,为1*5*2*4*3*3=360
    int par=lt.parallelStream().map(Item::getVal).reduce(1,(a,b)->a+b,(a,b)->a*b);

    System.out.println(search);//[1, 3]
    
    System.out.println(sum);//5
    
    System.out.println(par);//360

    lt.stream().peek(item->item.val++).filter(item->item.val>1).forEach(item->item.val++);
    for(Item item:lt)
      System.out.print(item.val+" ");//1 6 3 5 4 4 


  }
}
有状态和无状态
有状态 有状态就是有数据存储功能,线程不安全
无状态 无状态就是一次操作,不能保存数据。线程安全
有状态的有distinct,sorted,limit,skip。无状态的有filter,map,peek,unordered等。

短路和非短路
短路的操作有anyMatch,allMatch,findAny,findFIrst,noneMatch ,
非短路的操作有max,min,count,collect,reduce,toArray,forEach,forEachOrdered。

java NIO

同步非阻塞IO

NIO的部分

包含Selector

img.png

Java NIO 系统的核心在于:通道(Channel)和缓冲区(Buffer)与Selector(选择区)
Channel 负责传输,Buffer 负责存储,数据可以从Channel读到Buffer中,也可以从Buffer写到Channel中
Selector用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个线程可以监听多个数据通道。


Buffer可以当做缓存数组,不过提供了一些特殊的方法,IO的核心在于Channel
Buffer包括四个重要属性:
  position读或写的位置指针
  limit读或写的上限,写时等于capacity,读时为写的了多少
  capacity容量
  mark记录一个position,reset时就reset到这

Channel实现类包括FileChannel,SocketChannel,ServerSocketChannel,DatagramChannel
而如FileInputStream,Socket也有getChannel的方法
支持分散读取与聚集写入(按顺序填满多个Buffer,与多个Buffer按顺序写入Channel)

Selector用于监听channel
让channel调用方法(register)记住selector,当有输入时就会通知channel,这是一种设计模式的运用
无输入返回0,有则可以获取一个列表    
//粘贴
public static void test4() throws Exception {
    RandomAccessFile raf = new RandomAccessFile("a.txt", "rw");
    // 获取通道
    FileChannel channel = raf.getChannel();
    // 分配指定大小缓冲区
    ByteBuffer buf1 = ByteBuffer.allocate(2);
    ByteBuffer buf2 = ByteBuffer.allocate(1024);
    // 分散读取
    ByteBuffer[] bufs = {buf1, buf2};
    channel.read(bufs);  // 参数需要一个数组
    for (ByteBuffer byteBuffer : bufs) {
        byteBuffer.flip();  // 切换到读模式
    }
    System.out.println(new String(bufs[0].array(), 0, bufs[0].limit()));  // 打印 he
    System.out.println(new String(bufs[1].array(), 0, bufs[1].limit()));  // 打印 llo

    // 聚集写入
    RandomAccessFile raf2 = new RandomAccessFile("e.txt","rw");
    // 获取通道
    FileChannel channel2 = raf2.getChannel();
    channel2.write(bufs);  // 把 bufs 里面的几个缓冲区聚集到 channel2 这个通道中,聚集到通道中,也就是到了 e.txt 文件中
    channel2.close();
}
//Selector涉及的代码比较多,以后有空再看

关于多线程

Callable

由于有返回值,所以需要Future对象包装一下负责管理返回值,除此之外Future还能监听线程执行状态
,可以看到FutureTask类有run方法

Thread

.join则让某线程等待该线程执行,.setDameon设置守护线程,除了低优先度,还随主线程结束而结束
,.interrupt等,发出中断信号,线程根据情况处理(中断,继续等),yield运行变就绪,sleep不会放锁

util.concurrent.atomic

原子变量,操作都是原子的,原理为CAS

线程间通讯

  object.wait,Condition.await,BlockingQueue
  关于obj,上锁和通信要基于同一Obj
  关于lock,上锁要基于同一lock,通讯要基于同一condition
  另外,notify会导致释放锁,而singal不会

synchronized与Lock的区别

前者在jvm层面实现锁,后者在代码层面实现锁
前者可以写在代码块上,方法上,后者只能写在代码里
前者自动放锁,后者需要手动,所以要写在finally里
前者线程可能一直等待,后者可以设置超时时间
前者无法知道是否获取成功,后者可以通过tryLock得知(非阻塞,失败返回false,可设置等待时间)
前者锁可重入,不可中断,非公平,后者锁可重入、可中断、可公平/不公平,并可以细分读写锁以提高效率

锁可重入指可重复获取锁,不可中断指的是在等待锁的时候不能被Thread.interrupt
rlock可中断是方法lockInterruptibly(),它会使等待时收到中断信息后停止等待
公平锁按先后,非公平按抢
读写锁,其中写锁会看有没有占用读/写锁,有则停;而读则只看写锁的占用
public class Main{
    public static void main(String args[]){
      muti t1=new muti(0,0);
      muti t2=new muti(100,1);
      new Thread(t1).start();
      new Thread(t2).start();
    }
}
class muti implements Runnable{

    public static volatile AtomicInteger val=new AtomicInteger(0);
    public static ReentrantLock lock=new ReentrantLock();
    public static Condition cd=lock.newCondition();
    public static ReentrantReadWriteLock rwlock=new ReentrantReadWriteLock();
    public int eq;
    public int tid;
    public muti(int i,int tid){ this.eq=i; this.tid=tid;}
    public void run(){

        synchronized (val){
            try {
                System.out.println("线程" + tid + "开始");

                if(tid==0)
                    val.wait();
                else
                    val.notify();//这个会释放锁,如果是new Object().notify(),就会退出这里执行后面的

                System.out.println("线程" + tid + "结束");
            }catch (Exception e){}
        }



         try {
             lock.lock();

             System.out.println("线程" + tid + "开始");
             if(tid==0)
                 cd.await();
             else
                 cd.signal();//这里不会释放锁
             System.out.println("线程" + tid + "结束");
         }catch (Exception e){}finally {
             lock.unlock();
         }

        if(tid==0){
            try{
                rwlock.readLock().lock();
                System.out.println("读1");
                Thread.sleep(1000);
                System.out.println("读1");
            }catch (Exception e){}finally {
                rwlock.readLock().unlock();
            }
        }else{
            
            try{
                rwlock.writeLock().lock();
                System.out.println("写1");
                Thread.sleep(1000);
                System.out.println("写1");
            }catch (Exception e){}finally {
                rwlock.writeLock().unlock();
            }
        }

    };
}

synchronized底层

synchronized底层

在jvm里,对象在内存中分三块:对象头,实例数据,对齐填充
其中对象头包括Mark word,类型指针,数组长度(如果是数组)
其中Mark word存的运行时数据,包括hashcode,锁状态,持有的锁等

当加锁时,就让锁对象的Mark Word里所信息指向对应的线程

作用在代码块时,是让锁对象作位监视器锁monitor(每一个Java对象都有成为Monitor的潜质),
对应的指令是monitorenter(尝试占用monitor)与monitorexit,后者会出现两次

作用在方法时,通过判断是否有标志位ACC_SYNCHRONIZED,有的话则去争取monitor对象

自适应自旋锁:自旋一定次数后都无法获取则挂起,自旋的次数不是固定的,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。
锁消除:jvm编译时扫描上下文,去除没必要的锁
锁粗化:把连续加锁解锁操作连接在一起,形成一个范围更大的锁

锁的等级:无锁,偏向锁,轻量级锁,重量级锁(只升级不降级)
偏向锁:一个线程持有对象后,对象进入偏向模式,并记住该线程id,当同一线程再进入,对比id,就不许要申请锁,减少时间开销
轻量级锁:获取偏向锁失败,即出现了多个线程,则Mark word变成轻量级锁结构,并在线程的栈帧中创建一个锁记录,
        用CAS的方式让Mark word的锁指针指向这个拷贝,失败则自旋,自旋失败则阻塞,并升级为重量级锁
重量级锁:线程阻塞

ReentrantLock底层

RenntrantLock底层

CAS+AQS队列

关于AQS
  功能分独占锁和共享锁(允许多个线程同时获取锁)
  维护一个等待的FIFO队列(双向),当线程尝试CAS的形式获取锁失败后,加入FIFO队列并等待
  当锁被释放,则队首的线程被唤醒,尝试获取锁,当为非公平锁,则可能被其他刚好申请锁的线程占领
  当为公平锁,则新线程只会被加到队尾

  有一个state变量,大于1代表被占用,即有锁
  ReentrantReadWriteLock在使用时,稍微有些不同,int型state用二进制表示是32位,前16位(高位)表示为读锁,后面的16位(低位)表示为写锁

  另外还有一个单向的条件队列,被唤醒后会加入FIFO队列队尾

内部类FairSync(公平锁)和NonFairSync(非公平锁)

LongAdder

LongAdder

高并发下计数,atomic的性能变低

LongAdder是在发现竞争时,把数写入一个数组里的一个cell里,最后求和,是空间换时间的思想

Concurrent包

包括atomic,locks(Reentrantlock,AQS,Condition等),线程池Executors,阻塞队列BlockingQueque,并发map,同步器(协助线程同步,FutureTask等)

ThreadLocal

  • 记录线程本地数据,并且不让其他线程修改数据

    可以理解成每个线程都有自己专属的存储容器

    可以用来储存JDBC连接,或者管理对话Session

  • 本质类似HashMap,key为弱引用,这样在强引用没后ThreadLocal好被回收

    这里比较鬼畜,Entry是继承了WeakReference<ThreadLocal<?>>的类,但不代表它是ThreadLocal的子类,而是代表它有一个refernce指向这个ThreadLocal,所以认为Key是弱引用,而不是Entry是弱引用

    然后是为啥设置为弱引用,因为每个线程都有ThreadMap,而ThreadLocal被删了的话,在map仍然存在时,key仍然指向ThreadLocal,导致ThreadLocal不会被回收,所以只有设置为弱引用

  • 内存泄露问题

    比如一个ThreadLocal使用完了,它被设置为null了,按照上面所说,Key也变null了,但是value还在,并且后面删不了,导致内存泄露,所以一个ThreadLocal用完了要记得先把map的remove方法都调用一遍(说到底是弱引用的锅)

  • 关于hash

    ThreadMap的hash,遇到冲突就查找下一个位置,所以ThreadLocal最好设置地少点

public class ThreadLocalTest02 {

    public static void main(String[] args) {

        ThreadLocal<String> local = new ThreadLocal<>();

        IntStream.range(0, 10).forEach(i -> new Thread(() -> {
            local.set(Thread.currentThread().getName() + ":" + i);
            System.out.println("线程:" + Thread.currentThread().getName() + ",local:" + local.get());
        }).start());
    }
}

//输出结果:
//线程:Thread-0,local:Thread-0:0
//线程:Thread-1,local:Thread-1:1
//线程:Thread-2,local:Thread-2:2
//线程:Thread-3,local:Thread-3:3
//线程:Thread-4,local:Thread-4:4
//线程:Thread-5,local:Thread-5:5
//线程:Thread-6,local:Thread-6:6
//线程:Thread-7,local:Thread-7:7
//线程:Thread-8,local:Thread-8:8
//线程:Thread-9,local:Thread-9:9

public class test{
    public static void main(String args[]){
      class A{
        public int a;
        public A(int a){this.a=a;}
        public int get(){return a;}
      }

      class We extends WeakReference<A>{
        public int b=0;

        public We(A referent) {
          super(referent);
        }

        public We(A referent,int i){
          super(referent);
          this.b=i;
        }

      }

      A a=new A(1);
      We we=new We(a,2);
      a=null;
      System.gc();

      try{
        Thread.sleep(1000);
      }catch (Exception e){}

      System.out.println(we.b);//2
      System.out.println(we.get().a);//报空指针错误
    }
}

线程池原理

线程池状态变化

Running,运行状态,可接受新任务,可处理阻塞队列里的任务,调用shutdown方法进入SHUTDOWN,调用shutdownNow进入STOP

SHUTDOWN,待关闭状态,不接受新任务,继续处理阻塞队列,队列空后且工作线程数为0是进入TIDYING状态

STOP,停止状态,不接受新任务和处理阻塞队列,并尝试结束执行中任务,0->SHUTDOWN

TIDYING,整理状态,执行terminated后进入TERMINATED状态

TERMINATED,终止状态,所有资源已释放

重要属性
线程池状态(如上)与工作线程数:被储存到一个atomicInteger里,前3位状态,后29位数量

corePoolSize(核心工作线程数):当向线程池提交一个任务时,若线程池已创建的线程数小于corePoolSize,即便此时存在空闲线程,
也会通过创建一个新线程来执行该任务,直到已创建的线程数大于或等于corePoolSize时。

maximumPoolSize(最大线程数):线程池所允许的最大线程个数。当队列满了,且已创建的线程数小于maximumPoolSize,
则线程池会创建新的线程来执行任务。另外,对于无界队列,可忽略该参数。

keepAliveTime(多余线程存活时间):当线程池中线程数大于核心线程数时,线程的空闲时间如果超过线程存活时间,
那么这个线程就会被销毁,直到线程池中的线程数小于等于核心线程数。

workQueue(队列):用于传输和保存等待执行任务的阻塞队列。

threadFactory(线程创建工厂):用于创建新线程。threadFactory创建的线程也是采用new Thread()方式,
threadFactory创建的线程名都具有统一的风格:pool-m-thread-n(m为线程池的编号,n为线程池内的线程编号)。

handler(拒绝策略):当线程池和队列都满了,再加入线程会执行此策略

线程池的拒绝策略(当达到最大线程数时):
  AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。

  DiscardPolicy:也是丢弃任务,但是不抛出异常。
  
  DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复该过程)。
  
  CallerRunsPolicy:由调用线程处理该任务。

线程池大小设置
    CPU密集型:CPU核心数+1,由于CPU使用率高,避免CPU过度切换
    IO密集型:核心数*2,可以在等待IO时切换到其他线程,提高CPU使用率
    混合型:可以考虑分类成IO与CPU型任务,用不同的线程池处理;但如果拆分后执行时间差距大,则没有意义,不如不拆分

JVM

未完待续

在JDK8,是无法GC方法区

jvm启动过程
    jvm装入环境和配置:java.exe按顺序找JRE(本目录->父目录->注册表)
    装载jvm:找到JRE路径后,通过LoadJavaVM装入JVM文件....
    初始化JVM,获得本地调用接口
    运行java程序:对于class文件,直接调用javac的LoadClass状态类,对于jar包,先调用函数获取主类,再按class文件处理

ClassLoader:分三类
  BootStrap~,负责加载核心类库,C++编写,嵌入JVM内核
  Extension~,负责加载Java扩展类库
  APP~,加载引用程序classpath下的所有jar和class
  另外可根据自身需要自定义的ClassLoader

类加载执行顺序
1、加载:寻找并导入Class文件的二进制信息
2、连接:进行验证、准备和解析
  1)验证:确保导入类型的正确性
  2)准备:为类型分配内存并初始化为默认值(静态变量)
  3)解析:将字符引用解析为直接引用
3、初始化:调用Java代码,初始化类变量为指定初始值

方法区
类信息,运行时常量池等
包括方法编译出的字节码

Java栈与本地方法栈
  Java栈里只存基础类型的数据和对象的引用
  本地栈是存native方法的栈(它们不由java编写,所以不是管理jvm运行)

堆和GC

JVM对堆采用了分代算法,年轻代用赋值算法,老年代采用标记整理算法(用于GC)

  • 年轻代:主要存放新创建的对象,内存较小,GC较频繁,分为伊甸园和两个(from,to)幸存者区

    • 产生新对象会先放在伊甸园,如果空间不够则会发生young GC
    • young gc流程:扫描伊甸园和from区,如果对象存活,则把其复制到to区,to区满则复制到老年代,然后清空伊甸园和from区,最后交换from,to区的角色
    • 在幸存者区的对象有年龄计数器,每通过一次GC就+1,达到一定次数(默认15,可设置)就进入老年代
  • 老年代:用于存放生命周期长的对象

    • 当young gc后对象进入老年代,但老年代空间不够,就会发生majorGC;或者无法找到足够大的连续空间创建较大对象时也会提前触发一次old GC。老年代也装不下时会触发OOM异常
    • old GC:标志整理算法:先扫描一次标记出存活对象,再回收没有标记的对象,由于会产生内存碎片,所以会把所有存活对象向一段移动,再清除边界以外的内存(另外还有不好用的标记复制,标记清除)。
  • 永生代:内存上跟在堆后面,在JAVA8里已没有,被替换为元空间

  • GC

    • 判断对象回收条件:没有引用/作用域发生未捕获异常/作用域正常执行完毕/执行System.exit()/程序意外终止
    • Full GC,没有官方的定义,大众认知上为回收整个堆的空间可被回收的对象
    • stop the world:在运行到安全点后,挂起,然后gc
    • 根搜索算法:从一系列GC Roots的对象出发走引用链,没被走到的对象则可回收
  • 其他

    • 新生代和老年代各有对应的几种收集器

元空间

与永生代的区别:不再用虚拟机设置的内存,而是用本地内存(这里指的是堆外内存);并且字符串常量和静态变量在堆中,不在元空间里

与方法区的关系:以前的方法区的实现是永生代,现在的是元空间

程序计数器

当执行到native方法时,计数器为空

本地方法接口

实现Java程序与其他语言库、硬件、操作系统的交互,缺点是不再跨平台和安全

关于实例化

顺序是静态变量->静态代码块->变量->代码块->构造器

包含父类下则是父类静态->子类静态->父类非静态->子类非静态->父类构造器->子类构造器

双亲委派模型

要求除了根加载器每个加载器都要有父类,当要加载一个类时,首先尝试把任务委托给父类,如果父类不行,则尝试当前类加载

内存泄露和内存溢出

内存泄露的根本源因是长生命周期对象持有不被需要的短生命周期的引用,分4中
常发性~/偶发性/一次性:导致内存泄露的代码执行多次/少次/一次
隐式内存泄露:内存的释放被拖到程序结束时或者很久后,尽管早就没有用了

内存溢出:申请内存大于可用内存

垃圾回收器与一些参数

名称解释分类
Serial最早的单线程串行垃圾回收器年轻代
Serial OldSerial的老年代版本,标记整理老年代
ParNewSerial的多线程版本年轻代
Parallel多线程吞吐量优先的收集器(牺牲等待时间)年轻代
Parallel Old老年代版本,标记整理老年代
CMS以获取最短停顿时间为目标的收集器,适合B/S,基于标记清除算法,写屏障 + 增量更新老年代
G1兼顾等待时间和吞吐量,JDK9后的默认GC选项,写屏障 + SATB整堆

三色标记和读写屏障

三色标记法:从GCroot出发,默认白色,访问到该节点,但所有子节点未访问完为灰色,访问完则变黑色
如果是stop the world,不会出现什么问题,而如果要支持应用程序代码的并行,则会出现两种问题
  多标-浮动垃圾:
    比如访问到对象(灰色)后,把引用链断了,那么这个对象该被回收,但这次不会被回收。不过在下一次会被回收
    对于新对象,直接标为黑色,就不会在本轮被清楚,虽然可能期间垃圾
  漏标-读写屏障:
    就是比如黑色D指向灰色E,E指向白色G,此时(1)读并(2)断开E->G,(3)连接D->G,那么G就不该被清除,但它已经无法被标色了,三个条件破坏任意一个就行
    写屏障+SATB(针对2):把要改变的引用对象记录下来,相当于是保存了以前链接关系的快照
    写屏障+增量更新(针对3):记录新的引用对象,让GC在后面访问
    读屏障(针对1):即读取时就把引用对象记录下来,相当于增量版写屏障+SATB

默认空间老年代:新生代2:1,默认新生代中伊甸园和幸存者区比例8:1:1

JVM调优参数:-Xms 堆内存起始大小;-Xmx 最大的堆内存;-Xmn 新生代大小;-XX:SurviviorRatio:n 伊甸园与幸存者区的比例n:10-n…

跳过部分

编译过程,class加载详细过程,等等等等,感觉已经不是应届生该看的了,暂时跳过

暂时完结

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值