辰迅云帮助中心

帮助中心

bootstrap中怎么实现复选框,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。复选框(Checkbox)当创建表单时,如果您想让用户从列表中选择若干个选项时,请使用checkbox。(推荐学习:Bootstrap视频教程)对一系列复选框使用 .checkbox-inline ,控制它们显示在同一行上。复选框,可以设置 <div> 为父元素,类为 .custom-control 和 .custom-checkbox,复选框作为子元素放在该 <div> 里头,然后复选框设置为 type="checkbox",类为 .custom-control-input。复选框的文本使用 label 标签,标签使用 .custom-control-label 类,label 的 for 属性值需要匹配复选框的 id。实例<div class="checkbox">    <label><input type="checkbox" value="">选项 1</label></div><div class="checkbox">    <label><input type="checkbox" value="">选项 2</label></div>    #内联<div>    <label class="checkbox-inline">        <input type="checkbox" id="inlineCheckbox1" value="option1"> 选项 1    </label>    <label class="checkbox-inline">        <input type="checkbox" id="inlineCheckbox2" value="option2"> 选项 2    </label>    <label class="checkbox-inline">        <input type="checkbox" id="inlineCheckbox3" value="option3"> 选项 3    </label></div>关于bootstrap中怎么实现复选框问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注辰讯云资讯频道了解更多相关知识。...

这篇文章将为大家详细讲解有关linux中怎么将文本界面转化为图形界面,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。1、打开虚拟机,启动系统2、系统启动后,由于之前设置而进入文本界面,输入用户名和密码(注意:linux系统下密码不显示)3、然后输入指令startx4、在此界面选择continue5、成功的由文本界面转化为图形界面关于linux中怎么将文本界面转化为图形界面就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。...

Java 中GC的原理是什么

2021/6/23 22:37:52

这期内容当中小编将会给大家带来有关Java 中GC的原理是什么,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1 GC调优目标大多数情况下对 Java 程序进行GC调优, 主要关注两个目标:响应速度、吞吐量响应速度(Responsiveness) 响应速度指程序或系统对一个请求的响应有多迅速。比如,用户订单查询响应时间,对响应速度要求很高的系统,较大的停顿时间是不可接受的。调优的重点是在短的时间内快速响应吞吐量(Throughput) 吞吐量关注在一个特定时间段内应用系统的最大工作量,例如每小时批处理系统能完成的任务数量,在吞吐量方面优化的系统,较长的GC停顿时间也是可以接受的,因为高吞吐量应用更关心的是如何尽可能快地完成整个任务,不考虑快速响应用户请求GC调优中,GC导致的应用暂停时间影响系统响应速度,GC处理线程的CPU使用率影响系统吞吐量2 GC分代收集算法现代的垃圾收集器基本都是采用分代收集算法,其主要思想: 将Java的堆内存逻辑上分成两块:新生代、老年代,针对不同存活周期、不同大小的对象采取不同的垃圾回收策略新生代(Young Generation)新生代又叫年轻代,大多数对象在新生代中被创建,很多对象的生命周期很短。每次新生代的垃圾回收(又称Young GC、Minor GC、YGC)后只有少量对象存活,所以使用复制算法,只需少量的复制操作成本就可以完成回收新生代内又分三个区:一个Eden区,两个Survivor区(S0、S1,又称From Survivor、To Survivor),大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到两个Survivor区(中的一个)。当这个Survivor区满时,此区的存活且不满足晋升到老年代条件的对象将被复制到另外一个Survivor区。对象每经历一次复制,年龄加1,达到晋升年龄阈值后,转移到老年代老年代(Old Generation)在新生代中经历了N次垃圾回收后仍然存活的对象,就会被放到老年代,该区域中对象存活率高。老年代的垃圾回收通常使用“标记-整理”算法3 GC事件分类根据垃圾收集回收的区域不同,垃圾收集主要通常分为Young GC、Old GC、Full GC、Mixed GC(1) Young GC新生代内存的垃圾收集事件称为Young GC(又称Minor GC),当JVM无法为新对象分配在新生代内存空间时总会触发 Young GC,比如 Eden 区占满时。新对象分配频率越高, Young GC 的频率就越高Young GC 每次都会引起全线停顿(Stop-The-World),暂停所有的应用线程,停顿时间相对老年代GC的造成的停顿,几乎可以忽略不计(2) Old GC 、Full GC、Mixed GCOld GC,只清理老年代空间的GC事件,只有CMS的并发收集是这个模式 Full GC,清理整个堆的GC事件,包括新生代、老年代、元空间等Mixed GC,清理整个新生代以及部分老年代的GC,只有G1有这个模式4 GC日志分析GC日志是一个很重要的工具,它准确记录了每一次的GC的执行时间和执行结果,通过分析GC日志可以调优堆设置和GC设置,或者改进应用程序的对象分配模式,开启的JVM启动参数如下:-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps  -XX:+PrintGCTimeStamps常见的Young GC、Full GC日志含义如下:免费的GC日志图形分析工具推荐下面2个:GCViewer,下载jar包直接运行gceasy,web工具,上传GC日志在线使用5 内存分配策略Java提供的自动内存管理,可以归结为解决了对象的内存分配和回收的问题,前面已经介绍了内存回收,下面介绍几条最普遍的内存分配策略对象优先在Eden区分配 大多数情况下,对象在先新生代Eden区中分配。当Eden区没有足够空间进行分配时,虚拟机将发起一次Young GC大对象之间进入老年代 JVM提供了一个对象大小阈值参数(-XX:PretenureSizeThreshold,默认值为0,代表不管多大都是先在Eden中分配内存),大于参数设置的阈值值的对象直接在老年代分配,这样可以避免对象在Eden及两个Survivor直接发生大内存复制长期存活的对象将进入老年代 对象每经历一次垃圾回收,且没被回收掉,它的年龄就增加1,大于年龄阈值参数(-XX:MaxTenuringThreshold,默认15)的对象,将晋升到老年代中空间分配担保 当进行Young GC之前,JVM需要预估:老年代是否能够容纳Young GC后新生代晋升到老年代的存活对象,以确定是否需要提前触发GC回收老年代空间,基于空间分配担保策略来计算:continueSize:老年代最大可用连续空间Young GC之后如果成功(Young GC后晋升对象能放入老年代),则代表担保成功,不用再进行Full GC,提高性能;如果失败,则会出现“promotion failed”错误,代表担保失败,需要进行Full GC动态年龄判定 新生代对象的年龄可能没达到阈值(MaxTenuringThreshold参数指定)就晋升老年代,如果Young GC之后,新生代存活对象达到相同年龄所有对象大小的总和大于任一Survivor空间(S0 或 S1总空间)的一半,此时S0或者S1区即将容纳不了存活的新生代对象,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄另外,如果Young GC后S0或S1区不足以容纳:未达到晋升老年代条件的新生代存活对象,会导致这些存活对象直接进入老年代,需要尽量避免CMS原理及调优1 名词解释可达性分析算法:用于判断对象是否存活,基本思想是通过一系列称为“GC Root”的对象作为起点(常见的GC Root有系统类加载器、栈中的对象、处于激活状态的线程等),基于对象引用关系,从GC Roots开始向下搜索,所走过的路径称为引用链,当一个对象到GC Root没有任何引用链相连,证明对象不再存活Stop The World:GC过程中分析对象引用关系,为了保证分析结果的准确性,需要通过停顿所有Java执行线程,保证引用关系不再动态变化,该停顿事件称为Stop The World(STW)Safepoint:代码执行过程中的一些特殊位置,当线程执行到这些位置的时候,说明虚拟机当前的状态是安全的,如果有需要GC,线程可以在这个位置暂停。HotSpot采用主动中断的方式,让执行线程在运行期轮询是否需要暂停的标志,若需要则中断挂起2 CMS简介CMS(Concurrent Mark and Swee 并发-标记-清除),是一款基于并发、使用标记清除算法的垃圾回收算法,只针对老年代进行垃圾回收。CMS收集器工作时,尽可能让GC线程和用户线程并发执行,以达到降低STW时间的目的通过以下命令行参数,启用CMS垃圾收集器:-XX:+UseConcMarkSweepGC值得补充的是,下面介绍到的CMS GC是指老年代的GC,而Full GC指的是整个堆的GC事件,包括新生代、老年代、元空间等,两者有所区分3 新生代垃圾回收能与CMS搭配使用的新生代垃圾收集器有Serial收集器和ParNew收集器。这2个收集器都采用标记复制算法,都会触发STW事件,停止所有的应用线程。不同之处在于,Serial是单线程执行,ParNew是多线程执行4 老年代垃圾回收CMS GC以获取最小停顿时间为目的,尽可能减少STW时间,可以分为7个阶段阶段 1: 初始标记(Initial Mark)此阶段的目标是标记老年代中所有存活的对象, 包括 GC Root 的直接引用, 以及由新生代中存活对象所引用的对象,触发第一次STW事件这个过程是支持多线程的(JDK7之前单线程,JDK8之后并行,可通过参数CMSParallelInitialMarkEnabled调整)阶段 2: 并发标记(Concurrent Mark)此阶段GC线程和应用线程并发执行,遍历阶段1初始标记出来的存活对象,然后继续递归标记这些对象可达的对象阶段 3: 并发预清理(Concurrent Preclean)此阶段GC线程和应用线程也是并发执行,因为阶段2是与应用线程并发执行,可能有些引用关系已经发生改变。 通过卡片标记(Card Marking),提前把老年代空间逻辑划分为相等大小的区域(Card),如果引用关系发生改变,JVM会将发生改变的区域标记位“脏区”(Dirty Card),然后在本阶段,这些脏区会被找出来,刷新引用关系,清除“脏区”标记阶段 4: 并发可取消的预清理(Concurrent Abortable Preclean)此阶段也不停止应用线程. 本阶段尝试在 STW 的 最终标记阶段(Final Remark)之前尽可能地多做一些工作,以减少应用暂停时间 在该阶段不断循环处理:标记老年代的可达对象、扫描处理Dirty Card区域中的对象,循环的终止条件有: 1 达到循环次数 2 达到循环执行时间阈值 3 新生代内存使用率达到阈值阶段 5: 最终标记(Final Remark)这是GC事件中第二次(也是最后一次)STW阶段,目标是完成老年代中所有存活对象的标记。在此阶段执行: 1 遍历新生代对象,重新标记 2 根据GC Roots,重新标记 3 遍历老年代的Dirty Card,重新标记阶段 6: 并发清除(Concurrent Sweep)此阶段与应用程序并发执行,不需要STW停顿,根据标记结果清除垃圾对象阶段 7: 并发重置(Concurrent Reset)此阶段与应用程序并发执行,重置CMS算法相关的内部数据, 为下一次GC循环做准备5 CMS常见问题最终标记阶段停顿时间过长问题CMS的GC停顿时间约80%都在最终标记阶段(Final Remark),若该阶段停顿时间过长,常见原因是新生代对老年代的无效引用,在上一阶段的并发可取消预清理阶段中,执行阈值时间内未完成循环,来不及触发Young GC,清理这些无效引用通过添加参数:-XX:+CMSScavengeBeforeRemark。在执行最终操作之前先触发Young GC,从而减少新生代对老年代的无效引用,降低最终标记阶段的停顿,但如果在上个阶段(并发可取消的预清理)已触发Young GC,也会重复触发Young GC并发模式失败(concurrent mode failure) & 晋升失败(promotion failed)问题并发模式失败:当CMS在执行回收时,新生代发生垃圾回收,同时老年代又没有足够的空间容纳晋升的对象时,CMS 垃圾回收就会退化成单线程的Full GC。所有的应用线程都会被暂停,老年代中所有的无效对象都被回收晋升失败:当新生代发生垃圾回收,老年代有足够的空间可以容纳晋升的对象,但是由于空闲空间的碎片化,导致晋升失败,此时会触发单线程且带压缩动作的Full GC并发模式失败和晋升失败都会导致长时间的停顿,常见解决思路如下:降低触发CMS GC的阈值,即参数-XX:CMSInitiatingOccupancyFraction的值,让CMS GC尽早执行,以保证有足够的空间增加CMS线程数,即参数-XX:ConcGCThreads,增大老年代空间让对象尽量在新生代回收,避免进入老年代内存碎片问题通常CMS的GC过程基于标记清除算法,不带压缩动作,导致越来越多的内存碎片需要压缩,常见以下场景会触发内存碎片压缩:新生代Young GC出现新生代晋升担保失败(promotion failed)程序主动执行System.gc()可通过参数CMSFullGCsBeforeCompaction的值,设置多少次Full GC触发一次压缩,默认值为0,代表每次进入Full GC都会触发压缩,带压缩动作的算法为上面提到的单线程Serial Old算法,暂停时间(STW)时间非常长,需要尽可能减少压缩时间G1原理及调优1 G1简介G1(Garbage-First)是一款面向服务器的垃圾收集器,支持新生代和老年代空间的垃圾收集,主要针对配备多核处理器及大容量内存的机器,G1最主要的设计目标是: 实现可预期及可配置的STW停顿时间2 G1堆空间划分Region为实现大内存空间的低停顿时间的回收,将划分为多个大小相等的Region。每个小堆区都可能是 Eden区,Survivor区或者Old区,但是在同一时刻只能属于某个代在逻辑上, 所有的Eden区和Survivor区合起来就是新生代,所有的Old区合起来就是老年代,且新生代和老年代各自的内存Region区域由G1自动控制,不断变动巨型对象当对象大小超过Region的一半,则认为是巨型对象(Humongous Object),直接被分配到老年代的巨型对象区(Humongous regions),这些巨型区域是一个连续的区域集,每一个Region中最多有一个巨型对象,巨型对象可以占多个RegionG1把堆内存划分成一个个Region的意义在于:每次GC不必都去处理整个堆空间,而是每次只处理一部分Region,实现大容量内存的GC通过计算每个Region的回收价值,包括回收所需时间、可回收空间,在有限时间内尽可能回收更多的垃圾对象,把垃圾回收造成的停顿时间控制在预期配置的时间范围内,这也是G1名称的由来: garbage-first3 G1工作模式针对新生代和老年代,G1提供2种GC模式,Young GC和Mixed GC,两种会导致Stop The WorldYoung GC 当新生代的空间不足时,G1触发Young GC回收新生代空间 Young GC主要是对Eden区进行GC,它在Eden空间耗尽时触发,基于分代回收思想和复制算法,每次Young GC都会选定所有新生代的Region,同时计算下次Young GC所需的Eden区和Survivor区的空间,动态调整新生代所占Region个数来控制Young GC开销Mixed GC 当老年代空间达到阈值会触发Mixed GC,选定所有新生代里的Region,根据全局并发标记阶段(下面介绍到)统计得出收集收益高的若干老年代 Region。在用户指定的开销目标范围内,尽可能选择收益高的老年代Region进行GC,通过选择哪些老年代Region和选择多少Region来控制Mixed GC开销4 全局并发标记全局并发标记主要是为Mixed GC计算找出回收收益较高的Region区域,具体分为5个阶段阶段 1: 初始标记(Initial Mark) 暂停所有应用线程(STW),并发地进行标记从 GC Root 开始直接可达的对象(原生栈对象、全局对象、JNI 对象),当达到触发条件时,G1 并不会立即发起并发标记周期,而是等待下一次新生代收集,利用新生代收集的 STW 时间段,完成初始标记,这种方式称为借道(Piggybacking)阶段 2: 根区域扫描(Root Region Scan) 在初始标记暂停结束后,新生代收集也完成的对象复制到 Survivor 的工作,应用线程开始活跃起来; 此时为了保证标记算法的正确性,所有新复制到 Survivor 分区的对象,需要找出哪些对象存在对老年代对象的引用,把这些对象标记成根(Root); 这个过程称为根分区扫描(Root Region Scanning),同时扫描的 Suvivor 分区也被称为根分区(Root Region); 根分区扫描必须在下一次新生代垃圾收集启动前完成(接下来并发标记的过程中,可能会被若干次新生代垃圾收集打断),因为每次 GC 会产生新的存活对象集合阶段 3: 并发标记(Concurrent Marking) 标记线程与应用程序线程并行执行,标记各个堆中Region的存活对象信息,这个步骤可能被新的 Young GC 打断 所有的标记任务必须在堆满前就完成扫描,如果并发标记耗时很长,那么有可能在并发标记过程中,又经历了几次新生代收集阶段 4: 再次标记(Remark) 和CMS类似暂停所有应用线程(STW),以完成标记过程短暂地停止应用线程, 标记在并发标记阶段发生变化的对象,和所有未被标记的存活对象,同时完成存活数据计算阶段 5: 清理(Cleanup) 为即将到来的转移阶段做准备, 此阶段也为下一次标记执行所有必需的整理计算工作:整理更新每个Region各自的RSet(remember set,HashMap结构,记录有哪些老年代对象指向本Region,key为指向本Region的对象的引用,value为指向本Region的具体Card区域,通过RSet可以确定Region中对象存活信息,避免全堆扫描)回收不包含存活对象的Region统计计算回收收益高(基于释放空间和暂停目标)的老年代分区集合5 G1调优注意点Full GC问题G1的正常处理流程中没有Full GC,只有在垃圾回收处理不过来(或者主动触发)时才会出现, G1的Full GC就是单线程执行的Serial old gc,会导致非常长的STW,是调优的重点,需要尽量避免Full GC,常见原因如下:程序主动执行System.gc()全局并发标记期间老年代空间被填满(并发模式失败)Mixed GC期间老年代空间被填满(晋升失败)Young GC时Survivor空间和老年代没有足够空间容纳存活对象类似CMS,常见的解决是:增大-XX:ConcGCThreads=n 选项增加并发标记线程的数量,或者STW期间并行线程的数量:-XX:ParallelGCThreads=n减小-XX:InitiatingHeapOccupancyPercent 提前启动标记周期增大预留内存 -XX:G1ReservePercent=n ,默认值是10,代表使用10%的堆内存为预留内存,当Survivor区域没有足够空间容纳新晋升对象时会尝试使用预留内存巨型对象分配巨型对象区中的每个Region中包含一个巨型对象,剩余空间不再利用,导致空间碎片化,当G1没有合适空间分配巨型对象时,G1会启动串行Full GC来释放空间。可以通过增加 -XX:G1HeapRegionSize来增大Region大小,这样一来,相当一部分的巨型对象就不再是巨型对象了,而是采用普通的分配方式不要设置Young区的大小原因是为了尽量满足目标停顿时间,逻辑上的Young区会进行动态调整。如果设置了大小,则会覆盖掉并且会禁用掉对停顿时间的控制平均响应时间设置使用应用的平均响应时间作为参考来设置MaxGCPauseMillis,JVM会尽量去满足该条件,可能是90%的请求或者更多的响应时间在这之内, 但是并不代表是所有的请求都能满足,平均响应时间设置过小会导致频繁GC调优方法与思路如何分析系统JVM GC运行状况及合理优化?GC优化的核心思路在于:尽可能让对象在新生代中分配和回收,尽量避免过多对象进入老年代,导致对老年代频繁进行垃圾回收,同时给系统足够的内存减少新生代垃圾回收次数,进行系统分析和优化也是围绕着这个思路展开1 分析系统的运行状况系统每秒请求数、每个请求创建多少对象,占用多少内存Young GC触发频率、对象进入老年代的速率老年代占用内存、Full GC触发频率、Full GC触发的原因、长时间Full GC的原因常用工具如下:jstat jvm自带命令行工具,可用于统计内存分配速率、GC次数,GC耗时,常用命令格式jstat -gc <pid> <统计间隔时间>  <统计次数>输出返回值代表含义如下:例如: jstat -gc 32683 1000 10 ,统计pid=32683的进程,每秒统计1次,统计10次jmap jvm自带命令行工具,可用于了解系统运行时的对象分布,常用命令格式如下// 命令行输出类名、类数量数量,类占用内存大小,// 按照类占用内存大小降序排列jmap -histo <pid>// 生成堆内存转储快照,在当前目录下导出dump.hrpof的二进制文件,// 可以用eclipse的MAT图形化工具分析jmap -dump:live,format=b,file=dump.hprof <pid>jinfo 命令格式jinfo <pid>用来查看正在运行的 Java 应用程序的扩展参数,包括Java System属性和JVM命令行参数其他GC工具监控告警系统:Zabbix、Prometheus、Open-Falconjdk自动实时内存监控工具:VisualVM堆外内存监控: Java VisualVM安装Buffer Pools 插件、google perf工具、Java NMT(Native Memory Tracking)工具GC日志分析:GCViewer、gceasyGC参数检查和优化:http://xxfox.perfma.com/2 GC优化案例数据分析平台系统频繁Full GC平台主要对用户在APP中行为进行定时分析统计,并支持报表导出,使用CMS GC算法。数据分析师在使用中发现系统页面打开经常卡顿,通过jstat命令发现系统每次Young GC后大约有10%的存活对象进入老年代。原来是因为Survivor区空间设置过小,每次Young GC后存活对象在Survivor区域放不下,提前进入老年代,通过调大Survivor区,使得Survivor区可以容纳Young GC后存活对象,对象在Survivor区经历多次Young GC达到年龄阈值才进入老年代,调整之后每次Young GC后进入老年代的存活对象稳定运行时仅几百Kb,Full GC频率大大降低业务对接网关OOM网关主要消费Kafka数据,进行数据处理计算然后转发到另外的Kafka队列,系统运行几个小时候出现OOM,重启系统几个小时之后又OOM,通过jmap导出堆内存,在eclipse MAT工具分析才找出原因:代码中将某个业务Kafka的topic数据进行日志异步打印,该业务数据量较大,大量对象堆积在内存中等待被打印,导致OOM账号权限管理系统频繁长时间Full GC系统对外提供各种账号鉴权服务,使用时发现系统经常服务不可用,通过Zabbix的监控平台监控发现系统频繁发生长时间Full GC,且触发时老年代的堆内存通常并没有占满,发现原来是业务代码中调用了System.gc()上述就是小编为大家分享的Java 中GC的原理是什么了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注辰讯云资讯频道。...

HashMap、Hashtable、ConcurrentHashMap三者有什么区别,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。HashTable底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相关优化初始size为11,扩容:newsize = olesize*2+1计算index的方法:index = (hash & 0x7FFFFFFF) % tab.lengthHashMap底层数组+链表实现,可以存储null键和null值,线程不安全初始size为16,扩容:newsize = oldsize*2,size一定为2的n次幂扩容针对整个Map,每次扩容时,原来数组中的元素依次重新计算存放位置,并重新插入插入元素后才判断该不该扩容,有可能无效扩容(插入后如果扩容,如果没有再次插入,就会产生无效扩容)当Map中元素总数超过Entry数组的75%,触发扩容操作,为了减少链表长度,元素分配更均匀计算index方法:index = hash & (tab.length – 1)HashMap的初始值还要考虑加载因子: 哈希冲突:若干Key的哈希值按数组大小取模后,如果落在同一个数组下标上,将组成一条Entry链,对Key的查找需要遍历Entry链上的每个元素执行equals()比较。加载因子:为了降低哈希冲突的概率,默认当HashMap中的键值对达到数组大小的75%时,即会触发扩容。因此,如果预估容量是100,即需要设定100/0.75=134的数组大小。空间换时间:如果希望加快Key查找的时间,还可以进一步降低加载因子,加大初始大小,以降低哈希冲突的概率。HashMap和Hashtable都是用hash算法来决定其元素的存储,因此HashMap和Hashtable的hash表包含如下属性:容量(capacity):hash表中桶的数量初始化容量(initial capacity):创建hash表时桶的数量,HashMap允许在构造器中指定初始化容量尺寸(size):当前hash表中记录的数量负载因子(load factor):负载因子等于“size/capacity”。负载因子为0,表示空的hash表,0.5表示半满的散列表,依此类推。轻负载的散列表具有冲突少、适宜插入与查询的特点(但是使用Iterator迭代元素时比较慢)除此之外,hash表里还有一个“负载极限”,“负载极限”是一个0~1的数值,“负载极限”决定了hash表的最大填满程度。当hash表中的负载因子达到指定的“负载极限”时,hash表会自动成倍地增加容量(桶的数量),并将原有的对象重新分配,放入新的桶内,这称为rehashing。HashMap和Hashtable的构造器允许指定一个负载极限,HashMap和Hashtable默认的“负载极限”为0.75,这表明当该hash表的3/4已经被填满时,hash表会发生rehashing。“负载极限”的默认值(0.75)是时间和空间成本上的一种折中:较高的“负载极限”可以降低hash表所占用的内存空间,但会增加查询数据的时间开销,而查询是最频繁的操作(HashMap的get()与put()方法都要用到查询)较低的“负载极限”会提高查询数据的性能,但会增加hash表所占用的内存开销程序猿可以根据实际情况来调整“负载极限”值。ConcurrentHashMap底层采用分段的数组+链表实现,线程安全通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。(读操作不加锁,由于HashEntry的value变量是 volatile的,也能保证读取到最新的值。)Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁扩容:段内扩容(段内元素超过该段对应Entry数组长度的75%触发扩容,不会对整个Map进行扩容),插入前检测需不需要扩容,有效避免无效扩容Hashtable和HashMap都实现了Map接口,但是Hashtable的实现是基于Dictionary抽象类的。Java5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。HashMap基于哈希思想,实现对数据的读写。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,然后找到bucket位置来存储值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞时,对象将会储存在链表的下一个节点中。HashMap在每个链表节点中储存键值对对象。当两个不同的键对象的hashcode相同时,它们会储存在同一个bucket位置的链表中,可通过键对象的equals()方法来找到键值对。如果链表大小超过阈值(TREEIFY_THRESHOLD,8),链表就会被改造为树形结构。在HashMap中,null可以作为键,这样的键只有一个,但可以有一个或多个键所对应的值为null。当get()方法返回null值时,即可以表示HashMap中没有该key,也可以表示该key所对应的value为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个key,应该用containsKey()方法来判断。而在Hashtable中,无论是key还是value都不能为null。Hashtable是线程安全的,它的方法是同步的,可以直接用在多线程环境中。而HashMap则不是线程安全的,在多线程环境中,需要手动实现同步机制。Hashtable与HashMap另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。先看一下简单的类图:从类图中可以看出来在存储结构中ConcurrentHashMap比HashMap多出了一个类Segment,而Segment是一个可重入锁。ConcurrentHashMap是使用了锁分段技术来保证线程安全的。锁分段技术:首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。 ConcurrentHashMap提供了与Hashtable和SynchronizedMap不同的锁机制。Hashtable中采用的锁机制是一次锁住整个hash表,从而在同一时刻只能由一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶。ConcurrentHashMap默认将hash表分为16个桶,诸如get、put、remove等常用操作只锁住当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。...

Electron的原理是什么

2021/6/23 22:32:07

这篇文章将为大家详细讲解有关Electron的原理是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。        在Electron应用中,GUI组件仅在主进程可用,在辅助进程中不可用。那如果想要在辅助进程中试用GUI组件,势必需要和主进程进行通信。ipc模块就是用来实现主进程和辅助进程之间的通信。在主进程中使用ipcMain模块进行对辅助进程的通信进行控制和处理。而在辅助进程中,则使用ipcRenderer模块,来向主进程发送消息或者接受主进程的回应。接下来我们来讲讲这两个模块是如何进行通信的。        在主进程中使用ipcMain模块进行对辅助进程的通信进行控制和处理。ipcMain模块是EventEmitter的实例。ipcMain模块有四个监听方法:ipcMain.on(channel, listener)解析:监听事件channel,当新消息到达时,将执行监听方法listener(event, args)。ipcMain.once(channel, listener)解析:为事件channel添加一个只执行一次的监听方法listener。在第一次消息到大时会调用listener,执行完毕马上删除listener.ipcMain.removeListenner(channel, listener)解析:从特定的channel的监听事件中删除特定的listener监听者。ipcMain.removeAllListeners([channel])解析:本方法可接受传参channel,若传参channel,则删除特定channel的所有监听者。若未进行传参,则删除所有监听者。我们需要将event对象传递给callback,event主要有两个方法:同步回复消息和异步回复消息event.returnValue解析:当主进程需要向辅助进程回复同步消息,则可以使用该方法。event.sender.send('消息')解析:当主进程需要向辅助进程回复异步消息,则可以使用本方法。我们还是拿昨天的项目来继续用,我们来看看主进程和辅助进程之间如何进行通信的:首先主进程我们说过了就是index.js文件,我们在里面使用ipcMain模块对消息进行处理:const ipcMain = require('electron').ipcMain;ipcMain.on('test-message', function (event, arg) {    event.sender.send('test-reply', '这是主进程');});ipcMain.on('test-message', function (event, arg) {    event.returnValue = '这是主进程';});辅助进程目前只有一个index.html页面,我们在里面使用ipcRenderer对消息进行处理const ipcRenderer = require('electron').ipcRenderer;console.log(ipcRenderer.sendSync('test-message', '这是辅助进程'));ipcRenderer.on('test-reply', function(event, arg) {  console.log(arg); });ipcRenderer.send('test-message', '这是辅助进程');我们使用electron .命令测试一下效果:可以看到主进程成功进行通信了,那接下来我们来讲讲在辅助进程中ipcRenderer具体的使用方法吧。辅助进程其实就相当于我们一个个的html文件,可以使用ipcRenderer来接受和发送消息以便与主进程进行交互。ipcRenderer模块用来监听事件的有四个方法:ipcRenderer.on(channel, listener)ipcRenderer.once(channel, listener)ipcRenderer.removeListenner(channel, listener)ipcRenderer.removeAllListeners([channel])这四个方法的含义和ipcMain一致,这里就不进行重复解释了。发送消息ipcRenderer一样有发送同步消息和发送异步消息两个方法:ipcRenderer.send(channel, args)解析:辅助进程使用该方法向主进程发送异步消息。主进程使用ipcMain模块对异步消息进行处理。ipcRenderer.sendSync(channel, args)解析:辅助进程使用该方法向主进程发送同步消息。主进程使用ipcMain模块对同步消息进行处理然后通过event.returnValue进行相应消息给辅助进程。在这里我的建议是尽量少用同步消息,因为发送同步消息会阻塞整个页面渲染进程,对于用户体验来说是非常糟糕的一件事情。ipcRenderer其实还有一个方法用来发送消息:ipcRenderer.sendToHost(channel, args)关于Electron的原理是什么就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。...

WebSocket的原理是什么

2021/6/23 22:23:06

WebSocket的原理是什么,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。概念和原理WebSocket协议和HTTP协议一样,都是在ISO七层模型的最顶层——应用层。WebSocket允许服务器端主动向客户端推送数据。在WebSocket协议中,客户端浏览器和服务器只需要完成一次握手就可以创建持久性的连接,并在浏览器和服务器之间进行双向的数据传输——全双工通讯。HTTP和WebSocket连接生命周期对比图:WebSocket协议是通过HTTP协议来建立传输层TCP连接的web Socket请求头字段:通过Connection:upgrade和upgrade:websocket字段把http协议升级成websocket协议,所以在请求头中的Connection和Upgrade表示客户端发起的是WebSocket请求;同时请求头中还有Sec-WebSocket-Version字段表示客户端所使用的协议版本号,服务器会确认是否支持该版本号,如果支持了,服务端的响应就没有这个字段,如果不支持,响应的字段中就会有这个字段,对应的是服务端支持的版本号;Sec-WebSocket-Key是一个Base64编码值,由浏览器随机生成,用于升级request,服务端拿到这个编码值会把http协议升级成websocket协议Sec-WebSocket-Extensions表示客户端想表达的协议级的扩展;Web Socket响应头字段:HTTP/1.1 101 Switching procotols是一个切换协议,WebSocket协议通过HTTP协议来建立传输层的TCP连接;Connection和Upgrade,和请求字段一样;Sec-WebSocket-Accept: 表示服务器接受了客户端的请求,由Sec-Websocket-Key计算得来的,**计算方式:**将请求头中的Sec-WebSocket-Key和258EAFA5-E941-47DA-95CA-C5AB0DC85B11连接,然后进行SHA-1取哈希值,会得到一个20位的结果,然后再把这个结果用base64编码转换;优点和缺点优点:支持双向通讯,实时性更强;数据格式更轻量,性能开销小,通讯高效;因为http协议每次都要携带完整的头部,但是websocket在连接建立之后,从服务端到客户端只需要携带2-10个字节的头部,而从客户端到服务端也只需要2-10个字节的头部以及4个字节的掩码;支持扩展,用户可以扩展协议或者实现自定义好的子协议(比如支持自定义压缩算法等),美剧硅谷中的pied piper的压缩算法应用于直播技术缺点:少部分浏览器可能不支持,浏览器支持的程度与方式有区别;长连接对后端业务的代码稳定性要求更高,后端推送功能相对复杂;成熟的 HTTP生态下有大量的组件可以复用,WebSocket较少;应用场景:即时聊天通讯,网站消息通知,在线协同编辑,如腾讯文档;多玩家在线游戏,视频弹幕,股票基金实时报价;应用业务场景:实现网站私信功能方式一、使用AJAX轮询分析这种方式:可以设置请求时间间隔特别短(如200ms),可以让用户基本感受不到延时,能够完成功能,但是这样做对网络、服务器的浪费都特别大,1. 大量的HTTP请求响应,每次都要通过TCP三次握手建立连接然后再返回;2. 即便是没有消息,也要进行发送请求,后端Web服务器和WSGI服务器都要进行处理,如果用户量一大,这种方式的缺陷会非常明显;方式二、使用WebSocket建立连接分析这种方式:只需要建立一次连接即可,并且前端可以向后端推送,后端也可以向前端推送,并且是有消息了才会推送,没消息就不会推送,请求响应的头字节还小,优势非常明显;在django中应用这种技术需要考虑的问题:如何区别路由HTTP请求和WebSocket请求如何兼容django的认证系统(因为私信肯定是要登录的,所以需要认证)如果接收和推送WebSocket消息如何通过ORM保存和获取数据解决办法:使用django-channels或则dwebsocketdjango-channels是什么:django-channels是一个位django提供异步扩展的库,通常主要用来提供WebSocket支持和后台任务,因为django是一个同步框架。django同步框架图:一个请求来了,django处理过程中用户是需要等待的,重点是nginx会超时;所以,为了避免nginx超时,或者用户等待体验差,我们可以使用celery异步任务调度,把耗时的任务异步处理,让django先给nginx和用户返回一个结果。等任务处理完了,django并不能主动把结果推送出去,这时候就需要使用channels了。channels原理:请求流程图:从左向右,请求来了之后会按照类型分别访问不同的方向。channels的整体架构这个架构图中总共分成了三层:1. Interface Server是负责对协议进行解析,将不同协议分发到不同的Channel;2. Channel Layer是第二层,有了第1层的解析,请求可以分为http请求和websocket请求,这时候就要在Channel Layer这个频道层不同的队列中,可以是一个FIFO队列中进行缓冲排队,通常使用redis,不同的频道有不同的接收者监听; 3.Consumer消费者层,用来接收和处理频道层的消息;channels文件和配置含义asgi.py 是介于网络协议服务和Python应用之间的标准接口,能够处理多种通用协议类型,包括HTTP、HTTP2和WebSocket;如果没有websocket的网络协议项目部署只需要使用nginx+uWSGI+django就可以了,因为uWSGI服务器能够识别wsgi.py;但是如果有websocket的网络协议通讯项目,在部署的时候则就要使用到符合asgi接口标准的服务,例如daphne;channel_layers 需要在settings.py中配置,类似一个通道, 发送者(producer)在一端发送消息,消费者(consumer)在另一端监听;routings.py 相当于django中的urls.py,把http路由写在urls.py中,websocket请求写在routings.py中,与总的urls.py同级;consumers.py channels中的消费者,相当于django中的views.py,创建在每个app下;WSGI和ASGI的区别WSGI:Python Web Server Gateway Interface,为Python语言定义的Web服务器或框架之间的一种简单而通用的接口;ASGI:Asynchronous Server Gateway Interface, 异步网关服务接口,一个介于网络协议服务和Python应用直接的接口,能够处理多种通用的协议类型,如HTTP、HTTP2和WebSocket;区别:WSGI是基于HTTP协议模式的,不支持WebSocket,而ASGI就是为了支持Python常用的WSGI所不支持的新的协议标准,即ASGI是WSGI的扩展,而且能够通过asyncio异步运行;ASGI还可以支持chat protocols, loT protocols物联网协议等等…关于WebSocket的原理是什么问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注辰讯云资讯频道了解更多相关知识。...

今天就跟大家聊聊有关nginx中怎么通过配置ssl证书实现https访问,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。一,环境说明服务器系统:ubuntu16.04LTS服务器IP地址:47.89.12.99域名:bjubi.com二,域名解析到服务器在阿里云控制台-产品与服务-云解析DNS-找到需要解析的域名点“解析”,进入解析页面后选择【添加解析】按钮会弹出如下页面:主机记录这里选择@,记录值就是服务器ip地址,确认。三,申请ca证书在阿里云控制台-产品与服务-安全(云盾)-CA证书服务(数据安全),点击购买证书,选择“免费版DV SSL”,点击立即购买:然后点去支付:最后确认支付:就会回到管理界面:点击“补全”,输入要解析的域名,点下一步:说明:因为我们这里申请的是开发版免费证书,所以一个证书仅支持一个域名认证,不支持通配符。等待几分钟,证书状态变为“已签发”后,证书就申请成功了。四,下载证书列表中找到已签发的证书,下载:进入下载页面,找到ngin页签中nginx配置信息,并“下载证书 for Nginx”:记录以下内容,为了一会儿配置nginx用:下载的文件有两个:1,214292799730473.pem2,214292799730473.key五,服务器安装,配置nginx登录到服务器:$ apt-get update // 更新软件$ apt-get install nginx // 安装nginx1,nginx的安装目录为:/etc/nginx/。进入目录,增加cert/文件夹,把刚刚下载的两个文件上传到cert/文件夹中。2,在/etc/nginx/sites-enabled/下,增加bjubi.com文件。内容如下:说明:下面的配置是对443端口和80端口进行监听,443端口要启用ssl。监听443端口的server配置可以仿照上面ca认证页面的nginx配置示例进行配置。root节点笔者创建了一个bjubi.com/的文件夹,专门存放来自这个域名的请求以示区分。bjubi.com/文件夹下增加一个index.html文件,里面仅仅写了一行<h2>welcome。server {   listen 443;   server_name bjubi.com; // 你的域名  ssl on;   root /var/www/bjubi.com; // 前台文件存放文件夹,可改成别的  index index.html index.htm;// 上面配置的文件夹里面的index.html  ssl_certificate cert/214292799730473.pem;// 改成你的证书的名字  ssl_certificate_key cert/214292799730473.key;// 你的证书的名字  ssl_session_timeout 5m;   ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;   ssl_protocols TLSv1 TLSv1.1 TLSv1.2;   ssl_prefer_server_ciphers on;   location / {     index index.html index.htm;   } } server {   listen 80;   server_name bjubi.com;// 你的域名  rewrite ^(.*)$ https://$host$1 permanent;// 把http的域名请求转成https}配置完成后,检查一下nginx配置文件是否可用,有successful表示可用。$ nginx -t // 检查nginx配置文件配置正确后,重新加载配置文件使配置生效:$ nginx -s reload // 使配置生效至此,nginx的https访问就完成了,并且通过rewrite方式把所有http请求也转成了https请求,更加安全。如需重启nginx,用以下命令:$ service nginx stop // 停止$ service nginx start // 启动$ service nginx restart // 重启七,访问效果输入http:bjubi.com也会自动跳转至https页面。说明:如果是云服务器比如阿里云ECS,需要到阿里云ECS的管理后台的安全组,修改端口过滤规则把80端口和443端口开放才能访问到。看完上述内容,你们对nginx中怎么通过配置ssl证书实现https访问有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注辰讯云资讯频道,感谢大家的支持。...

Linux目录结构有什么用

2021/6/22 22:19:46

Linux目录结构有什么用,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Linux系统中“一切皆文件”是其最重要的设计思想,跟Git相似(因为毕竟是同一个人创造的,思路是一样的)。【常见目录说明】目录/bin存放二进制可执行文件(ls,cat,mkdir等),常用命令一般都在这里。/etc存放系统管理和配置文件/home存放所有用户文件的根目录,是用户主目录的基点,比如用户user的主目录就是/home/user,可以用~user表示/usr用于存放系统应用程序,比较重要的目录/usr/local 本地系统管理员软件安装目录(安装系统级的应用)。这是最庞大的目录,要用到的应用程序和文件几乎都在这个目录。/usr/x11r6 存放x window的目录/usr/bin 众多的应用程序  /usr/sbin 超级用户的一些管理程序  /usr/doc Linux文档  /usr/include linux下开发和编译应用程序所需要的头文件  /usr/lib 常用的动态链接库和软件包的配置文件  /usr/man 帮助文档  /usr/src 源代码,linux内核的源代码就放在/usr/src/linux里  /usr/local/bin 本地增加的命令  /usr/local/lib 本地增加的库/opt额外安装的可选应用程序包所放置的位置。一般情况下,我们可以把tomcat等都安装到这里。/proc虚拟文件系统目录,是系统内存的映射。可直接访问这个目录来获取系统信息。/root超级用户(系统管理员)的主目录(特权阶级^o^)/sbin存放二进制可执行文件,只有root才能访问。这里存放的是系统管理员使用的系统级别的管理命令和程序。如ifconfig等。/dev用于存放设备文件。/mnt系统管理员安装临时文件系统的安装点,系统提供这个目录是让用户临时挂载其他的文件系统。/boot存放用于系统引导时使用的各种文件/lib存放跟文件系统中的程序运行所需要的共享库及内核模块。共享库又叫动态链接共享库,作用类似windows里的.dll文件,存放了根文件系统程序运行所需的共享文件。/tmp用于存放各种临时文件,是公用的临时文件存储点。/var用于存放运行时需要改变数据的文件,也是某些大文件的溢出区,比方说各种服务的日志文件(系统启动日志等。)等。/lost+found这个目录平时是空的,系统非正常关机而留下“无家可归”的文件(windows下叫什么.chk)就在这里Linux目录和Windows目录有着很大的不同,Linux目录类似一个树,最顶层是其根目录:/1./bin目录/ b i n目录包含了引导启动所需的命令或普通用户可能用的命令(可能在引导启动后)。这些命令都是二进制文件的可执行程序( b i n是b i n a r y - -二进制的简称),多是系统中重要的系统文件。2. /sbin目录/ s b i n目录类似/bin ,也用于存储二进制文件。因为其中的大部分文件多是系统管理员使用的基本的系统程序,所以虽然普通用户必要且允许时可以使用,但一般不给普通用户使用。3. /etc目录/ e t c目录存放着各种系统配置文件,其中包括了用户信息文件/ e t c / p a s s w d,系统初始化文件/ e t c / r c等。正是这些文件才得以正常地运行。4. /root目录/root 目录是超级用户的目录。5. /lib目录/ l i b目录是根文件系统上的程序所需的共享库,存放了根文件系统程序运行所需的共享文件。这些文件包含了可被许多程序共享的代码,以避免每个程序都包含有相同的子程序的副本,故可以使得可执行文件变得更小,节省空间。6. /lib/modules 目录/lib/modules 目录包含系统核心可加载各种模块,尤其是那些在恢复损坏的系统时重新引导系统所需的模块(例如网络和文件系统驱动)。7. /dev目录/ d e v目录存放了设备文件,即设备驱动程序,用户通过这些文件访问外部设备。比如,用户可以通过访问/ d e v / m o u s e来访问鼠标的输入,就像访问其他文件一样。8. /tmp目录/tmp 目录存放程序在运行时产生的信息和数据。但在引导启动后,运行的程序最好使用/ v a r / t m p来代替/tmp ,因为前者可能拥有一个更大的磁盘空间。9. /boot目录/ b o o t目录存放引导加载器(bootstrap loader)使用的文件,如l i lo,核心映像也经常放在这里,而不是放在根目录中。但是如果有许多核心映像,这个目录就可能变得很大,这时使用单独的文件系统会更好一些。还有一点要注意的是,要确保核心映像必须在i d e硬盘的前1 0 2 4柱面内。10. /mnt目录/ m n t目录是系统管理员临时安装( m o u n t )文件系统的安装点。程序并不自动支持安装到/mnt 。/mnt 下面可以分为许多子目录,例如/mnt/dosa 可能是使用m s d o s文件系统的软驱,而/mnt/exta 可能是使用e x t 2文件系统的软驱,/mnt/cdrom 光驱等等。11. /proc, /usr,/var,/home目录关于Linux目录结构有什么用问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注辰讯云资讯频道了解更多相关知识。...

这期内容当中小编将会给大家带来有关JPA中orphanRemoval 属性有什么用,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。JPA定义Model关系中有orphanRemoval 这个参数。其作用您是否了解?orphanRemoval介绍和作用上面截图的jpa的官方文档以及jpa规范中明确说明,如果javaBean中父实体和子实体之间有一对一或一对多的级联关系的时候,如果我们想要删除父实体,也必须要级联删除子实体,需要被删除的级联关系中的子实体则被称为孤儿实体。orphanremoval属性的主要作用就是标记是否可以删除孤儿实体,假设我们这里有一个订单的案例,订单下面有许多行记录,假设我们删除其中的一行记录的话,需要被删除的这一行记录就被称作是孤儿,当我们设置这个属性为true的时候,意思就是我们可以从这个订单的所有记录中删除标记为孤儿的这条记录。属性详解和案例代码@OneToMany(mappedBy="parent",targetEntity=Child.class, cascade={CascadeType.ALL},orphanRemoval=true)privateList<Child> children= newArrayList<Child>();@OneToMany(mappedBy="parent",targetEntity=Son.class, cascade={CascadeType.ALL},orphanRemoval=true)privateList<Son> son= newArrayList<Son>();Fistly,we save One Parent have two Son and two Child in database:Parent p = newParent();Child c1 = newChild();Child c2 = newChild();Son s1 = newSon();Son s2 = newSon();p.setParentName("AAA");c1.setChildName("BBB");c2.setChildName("CCC");s1.setName("s1");s2.setName("s2");//set relationshipp.getChildren().add(c1);p.getChildren().add(c2);p.getSon().add(s1);p.getSon().add(s2);c1.setParent(p);c2.setParent(p);s1.setParent(p);s2.setParent(p);em.merge(p);so we can see it in database:mysql> select * from Parent;+----+------------+| id | parentName |+----+------------+| 1 | AAA |+----+------------+ mysql> select * from Child;+----+-----------+-----------+| id | childName | parent_id |+----+-----------+-----------+| 1 | BBB | 1 || 2 | CCC | 1 |+----+-----------+-----------+mysql> select * from Son;+----+------+-----------+| id | name | parent_id |+----+------+-----------+| 1 | s1 | 1 || 2 | s2 | 1 |+----+------+-----------+then we can use merge to remove orphaned entities .At first, we should ceate a test Data.Parent p = newParent();//set id to the entity,when there is correspongding record in database,just update.p.setId(1);Child c1 = newChild();c1.setId(1);c1.setChildName("c3");Son s2 = newSon();s2.setId(2);s2.setName("c4");//new Child and Son Enity,and add it to Parent's List.Without id,so jpa will save a new record for them ,id auto-increment.Child c = newChild();c.setChildName("childNew");c.setParent(p);p.getChildren().add(c); Son s = newSon();s.setName("sonNew");s.setParent(p);p.getSon().add(s);//at last,merge the root Entity Parent,when we set the orphanRemoval truein @OneToMany,the Child(id=2) Son(id=1) entity will be deleted when the line item is removed from the order.em.merge(p);then we can see it in database:mysql> select * from Parent;+----+------------+| id | parentName |+----+------------+| 1 | AAAnew |+----+------------+mysql> select * from Child;+----+-----------+-----------+| id | childName | parent_id |+----+-----------+-----------+| 1 | c3 | 1 || 3 | childNew | 1 |+----+-----------+-----------+record with id=2 is removed.mysql> select * from Son;+----+--------+-----------+| id | name | parent_id |+----+--------+-----------+| 2 | c4 | 1 || 3 | sonNew| 1 |+----+--------+-----------+record with id=1 is removed.与cascade的关系二者的作用范围不一样,cascade的作用范围是数据库,当cascade属性设置了delete时,当删除级联关系中的子集时,顺便也会将数据库中对应的数据删除。orphanremoval属性的作用范围仅仅是java应用代码中,做级联删除的操作也只适用于java实体代码范畴,它可以清楚javabean的级联关系,但并不能影响数据库的数据,只要cascade不点头是无法删除掉数据库的数据的上述就是小编为大家分享的JPA中orphanRemoval 属性有什么用了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注辰讯云资讯频道。...

@Qualifier的作用是什么

2021/6/22 22:16:24

@Qualifier的作用是什么,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。@Qualifier的作用这是官方的介绍This annotation may be used on a field or parameter as a qualifier for candidate beans when autowiring. It may also be used to annotate other custom annotations that can then in turn be used as qualifiers.简单的理解就是:在使用@Autowire自动注入的时候,加上@Qualifier(“test”)可以指定注入哪个对象;可以作为筛选的限定符,我们在做自定义注解时可以在其定义上增加@Qualifier,用来筛选需要的对象。这个理解看下面的代码吧,不好解释。功能介绍首先是对(1)的理解。//我们定义了两个TestClass对象,分别是testClass1和testClass2//我们如果在另外一个对象中直接使用@Autowire去注入的话,spring肯定不知道使用哪个对象//会排除异常 required a single bean, but 2 were found@Configurationpublic class TestConfiguration {   @Bean("testClass1")   TestClass testClass1(){       return new TestClass("TestClass1");    }   @Bean("testClass2")    TestClass testClass2(){       return new TestClass("TestClass2");    } }下面是正常的引用@RestControllerpublic class TestController {//此时这两个注解的连用就类似 @Resource(name="testClass1")@Autowired@Qualifier("testClass1")    private TestClass testClass;@GetMapping("/test")    public Object test(){return testClassList;     } }@Autowired和@Qualifier这两个注解的连用在这个位置就类似 @Resource(name=“testClass1”)对(2)的理解@Configurationpublic class TestConfiguration {//我们调整下在testClass1上增加@Qualifier注解@Qualifier@Bean("testClass1")    TestClass testClass1(){return new TestClass("TestClass1");     }     @Bean("testClass2")TestClass testClass2(){return new TestClass("TestClass2");     } }@RestControllerpublic class TestController {//我们这里使用一个list去接收testClass的对象@AutowiredList<TestClass> testClassList= Collections.emptyList();    @GetMapping("/test")public Object test(){return testClassList;     } }我们调用得到的结果是[      {"name": "TestClass1" },     {       "name": "TestClass2"} ]我们可以看到所有的testclass都获取到了。接下来我们修改下代码@RestControllerpublic class TestController {@Qualifier //我们在这增加注解@AutowiredList<TestClass> testClassList= Collections.emptyList();@GetMapping("/test")     public Object test(){return testClassList;     } }和上面代码对比就是在接收参数上增加了@Qualifier注解,这样看是有什么区别,我们调用下,结果如下:[      {"name": "TestClass1" } ]看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注辰讯云资讯频道,感谢您对辰讯云的支持。...