唯独性索引的值是唯独的,不错更快速的通过该索引来细目某笔纪录。举例,学生表中学号是具有唯独性的字段。为该字段耕作唯独性索引不错很快的细目某个学生的信息。如果使用姓名的话,可能存在同名时事,从而缩短查询速率。
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的法例不错大肆养息。
网站会员权益XXX已经成为一名顶尖体育明星,表现惊叹不已。曾,成功得益于一直以来努力坚持。 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类。
兑换皇冠app本篇著作东若是分析软援用、弱援用、虚援用的兑现,这三种援用类型都是收受于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步调则是皆备相同:
皇冠客服飞机:@seo3687
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步调的本领都相关联。
近日,仁爱礁附近的紧张局势再次成为焦点。菲律宾军舰在这片海域坐滩长达24年之久,严重侵犯了中国的主权。更令人不可容忍的是,有报道指出,菲律宾士兵对着中国海警船大笑并发出嘲讽,这种侮辱性的行为无疑给两国关系投下了一片阴影。面对此情况,我们需要冷静分析,并制定相应的对策。
WeakReferencepublic 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来兑现堆外内存回收的,后续会写篇著作来说说堆外内存的里里外外。