让建站和SEO变得简单

让不懂建站的用户快速建站,让会建站的提高建站效率!

最新资讯
你的位置:乐彩客购彩 > 最新资讯 >

口试中频繁被问到Java援用类型旨趣,带你深切剖释

发布日期:2022-03-13 19:13    点击次数:179
1.选用惟一性索引

惟一性索引的值是惟一的,不错更快速的通过该索引来详情某札记载。举例,学生表中学号是具有惟一性的字段。为该字段拓荒惟一性索引不错很快的详情某个学生的信息。如果使用姓名的话,可能存在同名形势,从而镌汰查询速率。

2.为频繁需要排序、分组和妥洽操作的字段拓荒索引

频繁需要ORDER BY、GROUP BY、DISTINCT和UNION等操作的字段,排序操作会损失好多时辰。如果为其拓荒索引,不错灵验地幸免排序操作。

3.为常四肢查询条目的字段拓荒索引

如果某个字段频繁用来做查询条目,那么该字段的查询速率会影响统统这个词表的查询速率。因此,为这么的字段拓荒索引,不错进步统统这个词表的查询速率。

4.铁心索引的数量

索引的数量不是越多越好。每个索引都需要占用磁盘空间,索引越多,需要的磁盘空间就越大。修改表时,对索引的重构和更新很贫乏。越多的索引,会使更新表变得很损失时辰。

5.尽量使用数据量少的索引

如果索引的值很长,那么查询的速率会受到影响。举例,对一个CHAR(100)类型的字段进行全文检索需要的时辰战胜要比对CHAR(10)类型的字段需要的时辰要多。

6.尽量使用前缀来索引

如果索引字段的值很长,最佳使用值的前缀来索引。举例,TEXT和BLOG类型的字段,进行全文检索会很损失时辰。如果只检索字段的前边的多少个字符,这么不错进步检索速率。

7.删除不再使用或者很少使用的索引

表中的数据被多数更新,或者数据的使用风物被改变后,原有的一些索引可能不再需要。数据库贬责员应当依期找出这些索引,将它们删除,从而减少索引对更新操作的影响。

8.最左前缀匹配原则,相配紧迫的原则。

mysql会一直向右匹配直到遭遇范围查询(>、<、between、like)就罢手匹配,比如a 1=”” and=”” b=”2” c=”“> 3 and d = 4 如果拓荒(a,b,c,d)规章的索引,d是用不到索引的,如果拓荒(a,b,d,c)的索引则都不错用到,a,b,d的规章不错恣意调换。

9.=和in不错乱序。

比如a = 1 and b = 2 and c = 3 拓荒(a,b,c)索引不错恣意规章,mysql的查询优化器会帮你优化成索引不错识别的风物

10.尽量选用分歧度高的列四肢索引。

分歧度的公式是count(distinct col)/count(*),示意字段不重叠的比例,比例越大咱们扫描的记载数越少,惟一键的分歧度是1,而一些景色、性别字段可能在大数据眼前分歧度就 是0,那可能有人会问,这个比例有什么西席值吗?使用场景不同,这个值也很难详情,一般需要join的字段咱们都要求是0.1以上,即平均1条扫描10条 记载

11.索引列不行参与猜度打算,保持列“干净”。

比如from_unixtime(create_time) = ’2014-05-29’就不行使用到索引,原因很浅易,b+树中存的都是数据表中的字段值,但进行检索时,需要把统统元素都讹诈函数才调比拟,彰着本钱 太大。是以语句应该写成create_time = unix_timestamp(’2014-05-29’);

12.尽量的膨大索引,不要新建索引。

比如表中仍是有a的索引,当今要加(a,b)的索引,那么只需要修改正本的索引即可

留神:选用索引的最终标的是为了使查询的速率变快。上头给出的原则是最基本的准则,但不行寂寥于上头的准则。读者要在以后的学习和职责中进行不断的现实。凭据讹诈的实质情况进行分析和判断,选用最合适的索引风物。

Java中一共有4种援用类型(其实还有一些其他的援用类型比如FinalReference):强援用、软援用、弱援用、虚援用。

其中强援用即是咱们频繁使用的Object a = new Object(); 这么的风物,在Java中并莫得对应的Reference类。

本篇著作东淌若分析软援用、弱援用、虚援用的兑现,这三种援用类型都是罗致于Reference这个类,主要逻辑也在Reference中。

问题

在分析前,先抛几个问题?

网上大多数著作关于软援用的先容是:在内存不及的工夫才会被回收,那内存不及是怎样界说的?什么才叫内存不及? 网上大多数著作关于虚援用的先容是:形同虚设,虚援用并不会决定对象的生命周期。主要用来追踪对象被垃圾回收器回收的行为。确切是这么吗? 虚援用在Jdk中有哪些场景下用到了呢? Reference

咱们先看下Reference.java中的几个字段

public abstract class Reference<T> {     //援用的对象     private T referent;             //回收部队,由使用者在Reference的构造函数中指定     volatile ReferenceQueue<? super T> queue;      //当该援用被加入到queue中的工夫,该字段被建立为queue中的下一个元素,以形成链表结构     volatile Reference next;     //在GC时,JVM底层会珍摄一个叫DiscoveredList的链表,存放的是Reference对象,discovered字段指向的即是链表中的下一个元素,由JVM建立     transient private Reference<T> discovered;       //进行线程同步的锁对象     static private class Lock { }     private static Lock lock = new Lock();     //恭候加入queue的Reference对象,在GC时由JVM建立,会有一个java层的线程(ReferenceHandler)延绵无间的从pending中提炼元素加入到queue     private static Reference<Object> pending = null; } 

一个Reference对象的生命周期如下:

主要分为Native层和Java层两个部分。

Native层在GC时将需要被回收的Reference对象加入到DiscoveredList中(代码在referenceProcessor.cpp中

process_discovered_references重要),然后将DiscoveredList的元素转移到PendingList中(代码在referenceProcessor.cpp中enqueue_discovered_ref_helper重要),PendingList的队首即是Reference类中的pending对象。

望望Java层的代码

private static class ReferenceHandler extends Thread {          ...         public void run() {             while (true) {                 tryHandlePending(true);             }         }   }  static boolean tryHandlePending(boolean waitForNotify) {         Reference<Object> r;         Cleaner c;         try {             synchronized (lock) {                 if (pending != null) {                     r = pending;                      //如果是Cleaner对象,则记载下来,底下做特地处理                     c = r instanceof Cleaner ? (Cleaner) r : null;                     //指向PendingList的下一个对象                     pending = r.discovered;                     r.discovered = null;                 } else {                    //如果pending为null就先恭候,当有对象加入到PendingList中时,jvm会现实notify                     if (waitForNotify) {                         lock.wait();                     }                     // retry if waited                     return waitForNotify;                 }             }         }          ...          // 如果时CLeaner对象,则调用clean重要进行资源回收         if (c != null) {             c.clean();             return true;         }         //将Reference加入到ReferenceQueue,开发者不错通过从ReferenceQueue中poll元素感知到对象被回收的事件。         ReferenceQueue<? super Object> q = r.queue;         if (q != ReferenceQueue.NULL) q.enqueue(r);         return true;  } 

历程比拟浅易:即是延绵无间的从PendingList中提炼出元素,然后将其加入到ReferenceQueue中去,开发者不错通过从ReferenceQueue中poll元素感知到对象被回收的事件。

另外需要留神的是,关于Cleaner类型(罗致自虚援用)的对象会有独特的处理:在其指向的对象被回收时,会调用clean重要,该重要主淌若用来做对应的资源回收,在堆外内存DirectByteBuffer中即是用Cleaner进行堆外内存的回收,这亦然虚援用在java中的典型讹诈。

看完毕Reference的兑现,再望望几个兑现类里,各自有什么不同。

SoftReference

public class SoftReference<T> extends Reference<T> {      static private long clock;      private long timestamp;      public SoftReference(T referent) {         super(referent);         this.timestamp = clock;     }      public SoftReference(T referent, ReferenceQueue<? super T> q) {         super(referent, q);         this.timestamp = clock;     }      public T get() {         T o = super.get();         if (o != null && this.timestamp != clock)             this.timestamp = clock;         return o;     }  } 

软援用的兑现很浅易,就多了两个字段:clock和timestamp。clock是个静态变量,每次GC时都会将该字段建立成刻下时辰。timestamp字段则会在每次调用get重要时将其赋值为clock(如果不卓越且对象没被回收)。

那这两个字段的作用是什么呢?这和软援用在内存不够的工夫才被回收,又有什么关系呢?

这些还得看JVM的源码才行,因为决定对象是否需要被回收都是在GC中兑现的。

size_t ReferenceProcessor::process_discovered_reflist(   DiscoveredList               refs_lists[],   ReferencePolicy*             policy,   bool                         clear_referent,   BoolObjectClosure*           is_alive,   OopClosure*                  keep_alive,   VoidClosure*                 complete_gc,   AbstractRefProcTaskExecutor* task_executor) {  ...    //还铭刻上文提到过的DiscoveredList吗?refs_lists即是DiscoveredList。    //关于DiscoveredList的处理分为几个阶段,SoftReference的处理就在第一阶段  ...       for (uint i = 0; i < _max_num_q; i++) {         process_phase1(refs_lists[i], policy,                        is_alive, keep_alive, complete_gc);       }  ... }  //该阶段的主要标的即是当内存有余时,将对应的SoftReference从refs_list中移除。 void ReferenceProcessor::process_phase1(DiscoveredList&    refs_list,                                    ReferencePolicy*   policy,                                    BoolObjectClosure* is_alive,                                    OopClosure*        keep_alive,                                    VoidClosure*       complete_gc) {    DiscoveredListIterator iter(refs_list, keep_alive, is_alive);   // Decide which softly reachable refs should be kept alive.   while (iter.has_next()) {     iter.load_ptrs(DEBUG_ONLY(!discovery_is_atomic() /* allow_null_referent */));     //判断援用的对象是否存活     bool referent_is_dead = (iter.referent() != NULL) && !iter.is_referent_alive();     //如果援用的对象仍是不存活了,则会去调用对应的ReferencePolicy判断该对象是经常要被回收     if (referent_is_dead &&         !policy->should_clear_reference(iter.obj(), _soft_ref_timestamp_clock)) {       if (TraceReferenceGC) {         gclog_or_tty->print_cr("Dropping reference (" INTPTR_FORMAT ": %s"  ") by policy",                                (void *)iter.obj(), iter.obj()->klass()->internal_name());       }       // Remove Reference object from list       iter.remove();       // Make the Reference object active again       iter.make_active();       // keep the referent around       iter.make_referent_alive();       iter.move_to_next();     } else {       iter.next();     }   }  ... } 

refs_lists中存放了本次GC发现的某种援用类型(虚援用、软援用、弱援用等),而

process_discovered_reflist重要的作用即是将不需要被回收的对象从refs_lists移裁撤,refs_lists终末剩下的元素全是需要被回收的元素,终末会将其第一个元素赋值给上文提到过的Reference.java#pending字段。

ReferencePolicy一共有4种兑现:NeverClearPolicy,AlwaysClearPolicy,LRUCurrentHeapPolicy,LRUMaxHeapPolicy。

其中NeverClearPolicy恒久复返false,代表恒久不回收SoftReference,在JVM中该类莫得被使用,AlwaysClearPolicy则恒久复返true,在referenceProcessor.hpp#setup重要中中不错建立policy为AlwaysClearPolicy,至于什么工夫会用到AlwaysClearPolicy,宇宙有敬爱敬爱不错自行计划。

LRUCurrentHeapPolicy和LRUMaxHeapPolicy的should_clear_reference重要则是透澈计议:

bool LRUMaxHeapPolicy::should_clear_reference(oop p,                                              jlong timestamp_clock) {   jlong interval = timestamp_clock - java_lang_ref_SoftReference::timestamp(p);   assert(interval >= 0, "Sanity check");    // The interval will be zero if the ref was accessed since the last scavenge/gc.   if(interval <= _max_interval) {     return false;   }    return true; } 

timestamp_clock即是SoftReference的静态字段clock,

java_lang_ref_SoftReference::timestamp(p)对应是字段timestamp。如果前次GC后有调用SoftReference#get,interval值为0,不然为多少次GC之间的时辰差。

_max_interval则代表了一个临界值,它的值在LRUCurrentHeapPolicy和LRUMaxHeapPolicy两种战略中有相反。

void LRUCurrentHeapPolicy::setup() {   _max_interval = (Universe::get_heap_free_at_last_gc() / M) * SoftRefLRUPolicyMSPerMB;   assert(_max_interval >= 0,"Sanity check"); }  void LRUMaxHeapPolicy::setup() {   size_t max_heap = MaxHeapSize;   max_heap -= Universe::get_heap_used_at_last_gc();   max_heap /= M;    _max_interval = max_heap * SoftRefLRUPolicyMSPerMB;   assert(_max_interval >= 0,"Sanity check"); } 

看到这里你就表现SoftReference到底什么工夫被被回收了,它和使用的战略(默许应该是LRUCurrentHeapPolicy),堆可用大小,该SoftReference上一次调用get重要的时辰都相相关。

WeakReference
public class WeakReference<T> extends Reference<T> {      public WeakReference(T referent) {         super(referent);     }      public WeakReference(T referent, ReferenceQueue<? super T> q) {         super(referent, q);     }  } 

不错看到,关于Soft references和Weak references clear_referent字段传入的都是true,这也稳妥咱们的预期:对象不可达后,援用字段就会被置为null,然后对象就会被回收(关于软援用来说,如果内存有余的话,在Phase 1,相关的援用就会从refs_list中被移除,到Phase 3时refs_list为空麇集)。

但关于Final references和 Phantom references,clear_referent字段传入的是false,也就意味着被这两种援用类型援用的对象,如果莫得其他独特处理,只须Reference对象还存活,那援用的对象是不会被回收的。Final references和对象是否重写了finalize重要联系,不在本文分析范围之内,咱们接下来望望Phantom references。

不错看到WeakReference在Java层仅仅罗致了Reference,莫得做任何的变调。那referent字段是什么工夫被置为null的呢?要搞明晰这个问题咱们再看下上文提到过的

process_discovered_reflist重要:

 

size_t ReferenceProcessor::process_discovered_reflist(   DiscoveredList               refs_lists[],   ReferencePolicy*             policy,   bool                         clear_referent,   BoolObjectClosure*           is_alive,   OopClosure*                  keep_alive,   VoidClosure*                 complete_gc,   AbstractRefProcTaskExecutor* task_executor) {  ...    //Phase 1:将统统不存活然而还不行被回收的软援用从refs_lists中移除(只好refs_lists为软援用的工夫,这里policy才不为null)   if (policy != NULL) {     if (mt_processing) {       RefProcPhase1Task phase1(*this, refs_lists, policy, true /*marks_oops_alive*/);       task_executor->execute(phase1);     } else {       for (uint i = 0; i < _max_num_q; i++) {         process_phase1(refs_lists[i], policy,                        is_alive, keep_alive, complete_gc);       }     }   } else { // policy == NULL     assert(refs_lists != _discoveredSoftRefs,            "Policy must be specified for soft references.");   }    // Phase 2:   // 移除统统指向对象还存活的援用   if (mt_processing) {     RefProcPhase2Task phase2(*this, refs_lists, !discovery_is_atomic() /*marks_oops_alive*/);     task_executor->execute(phase2);   } else {     for (uint i = 0; i < _max_num_q; i++) {       process_phase2(refs_lists[i], is_alive, keep_alive, complete_gc);     }   }    // Phase 3:   // 凭据clear_referent的值决定是否将不存活对象回收   if (mt_processing) {     RefProcPhase3Task phase3(*this, refs_lists, clear_referent, true /*marks_oops_alive*/);     task_executor->execute(phase3);   } else {     for (uint i = 0; i < _max_num_q; i++) {       process_phase3(refs_lists[i], clear_referent,                      is_alive, keep_alive, complete_gc);     }   }    return total_list_count; }  void ReferenceProcessor::process_phase3(DiscoveredList&    refs_list,                                    bool               clear_referent,                                    BoolObjectClosure* is_alive,                                    OopClosure*        keep_alive,                                    VoidClosure*       complete_gc) {   ResourceMark rm;   DiscoveredListIterator iter(refs_list, keep_alive, is_alive);   while (iter.has_next()) {     iter.update_discovered();     iter.load_ptrs(DEBUG_ONLY(false /* allow_null_referent */));     if (clear_referent) {       // NULL out referent pointer       //将Reference的referent字段置为null,之后会被GC回收       iter.clear_referent();     } else {       // keep the referent around       //记号援用的对象为存活,该对象在此次GC将不会被回收       iter.make_referent_alive();     }     ...   }     ... } 

无论是弱援用如故其他援用类型,将字段referent置null的操作都发生在process_phase3中,而具体行动是由clear_referent的值决定的。而clear_referent的值则和援用类型相关。

 

ReferenceProcessorStats ReferenceProcessor::process_discovered_references(   BoolObjectClosure*           is_alive,   OopClosure*                  keep_alive,   VoidClosure*                 complete_gc,   AbstractRefProcTaskExecutor* task_executor,   GCTimer*                     gc_timer) {   NOT_PRODUCT(verify_ok_to_handle_reflists());     ...   //process_discovered_reflist重要的第3个字段即是clear_referent   // Soft references   size_t soft_count = 0;   {     GCTraceTime tt("SoftReference", trace_time, false, gc_timer);     soft_count =       process_discovered_reflist(_discoveredSoftRefs, _current_soft_ref_policy, true,                                  is_alive, keep_alive, complete_gc, task_executor);   }    update_soft_ref_master_clock();    // Weak references   size_t weak_count = 0;   {     GCTraceTime tt("WeakReference", trace_time, false, gc_timer);     weak_count =       process_discovered_reflist(_discoveredWeakRefs, NULL, true,                                  is_alive, keep_alive, complete_gc, task_executor);   }    // Final references   size_t final_count = 0;   {     GCTraceTime tt("FinalReference", trace_time, false, gc_timer);     final_count =       process_discovered_reflist(_discoveredFinalRefs, NULL, false,                                  is_alive, keep_alive, complete_gc, task_executor);   }    // Phantom references   size_t phantom_count = 0;   {     GCTraceTime tt("PhantomReference", trace_time, false, gc_timer);     phantom_count =       process_discovered_reflist(_discoveredPhantomRefs, NULL, false,                                  is_alive, keep_alive, complete_gc, task_executor);   }     ... } 

不错看到,关于Soft references和Weak references clear_referent字段传入的都是true,这也稳妥咱们的预期:对象不可达后,援用字段就会被置为null,然后对象就会被回收(关于软援用来说,如果内存有余的话,在Phase 1,相关的援用就会从refs_list中被移除,到Phase 3时refs_list为空麇集)。

但关于Final references和 Phantom references,clear_referent字段传入的是false,也就意味着被这两种援用类型援用的对象,如果莫得其他独特处理,只须Reference对象还存活,那援用的对象是不会被回收的。Final references和对象是否重写了finalize重要联系,不在本文分析范围之内,咱们接下来望望Phantom references。

PhantomReference

 

public class PhantomReference<T> extends Reference<T> {      public T get() {         return null;     }      public PhantomReference(T referent, ReferenceQueue<? super T> q) {         super(referent, q);     }  } 

不错看到虚援用的get重要恒久复返null,咱们看个demo。

public static void demo() throws InterruptedException {         Object obj = new Object();         ReferenceQueue<Object> refQueue =new ReferenceQueue<>();         PhantomReference<Object> phanRef =new PhantomReference<>(obj, refQueue);          Object objg = phanRef.get();         //这里拿到的是null         System.out.println(objg);         //让obj变成垃圾         obj=null;         System.gc();         Thread.sleep(3000);         //gc后会将phanRef加入到refQueue中         Reference<? extends Object> phanRefP = refQueue.remove();          //这里输出true         System.out.println(phanRefP==phanRef);     } 

从以上代码中不错看到,虚援用约略在指向对象不可达时得到一个'见告'(其实统统罗致References的类都有这个功能),需要留神的是GC完成后,phanRef.referent依然指向之前创建Object,也即是说Object对象一直没被回收!

而变成这一形势的原因在上一末节末尾仍是说了:关于Final references和 Phantom references,clear_referent字段传入的时false,也就意味着被这两种援用类型援用的对象,如果莫得其他独特处理,在GC中是不会被回收的。

关于虚援用来说,从refQueue.remove();得到援用对象后,不错调用clear重要强行销毁援用和对象之间的关系,使得对象下次不错GC时不错被回收掉。

End

针对著作起首提倡的几个问题,看完分析,咱们仍是能给出酬金:

1.咱们频繁在网上看到软援用的先容是:在内存不及的工夫才会回收,那内存不及是怎样界说的?为什么才叫内存不及?

软援用会在内存不实时被回收,内存不及的界说和该援用对象get的时辰以及刻下堆可用内存大小都相相关,猜度打算公式在上文中也仍是给出。

2.网上关于虚援用的先容是:形同虚设,与其他几种援用都不同,虚援用并不会决定对象的生命周期。主要用来追踪对象被垃圾回收器回收的行为。确切是这么吗?

严格的说,虚援用是会影响对象生命周期的,如果不做任那儿理,只须虚援用不被回收,那其援用的对象恒久不会被回收。是以一般来说,从ReferenceQueue中赢得PhantomReference对象后,如果PhantomReference对象不会被回收的话(比如被其他GC ROOT可达的对象援用),需要调用clear重要销毁PhantomReference和其援用对象的援用关系。

3.虚援用在Jdk中有哪些场景下用到了呢?

DirectByteBuffer中是用虚援用的子类Cleaner.java来兑现堆外内存回收的,后续会写篇著作来说说堆外内存的里里外外。

 








    Powered by 乐彩客购彩 @2013-2022 RSS地图 HTML地图

    Copyright 站群 © 2013-2021 365建站器 版权所有