首页 文章 精选 留言 我的

精选列表

搜索[安全机制],共10000篇文章
优秀的个人博客,低调大师

Java垃圾回收机制(转)

说到垃圾回收(Garbage Collection,GC),很多人就会自然而然地把它和Java联系起来。在Java中,程序员不需要去关心内存动态分配和垃圾回收的问题,这一切都交给了JVM来处理。顾名思义,垃圾回收就是释放垃圾占用的空间,那么在Java中,什么样的对象会被认定为“垃圾”?那么当一些对象被确定为垃圾之后,采用什么样的策略来进行回收(释放空间)?在目前的商业虚拟机中,有哪些典型的垃圾收集器?下面我们就来逐一探讨这些问题。以下是本文的目录大纲: 一.如何确定某个对象是“垃圾”? 二.典型的垃圾收集算法 三.典型的垃圾收集器 一.如何确定某个对象是“垃圾”? 在这一小节我们先了解一个最基本的问题:如果确定某个对象是“垃圾”?既然垃圾收集器的任务是回收垃圾对象所占的空间供新的对象使用,那么垃圾收集器如何确定某个对象是“垃圾”?—即通过什么方法判断一个对象可以被回收了。 在java中是通过引用来和对象进行关联的,也就是说如果要操作对象,必须通过引用来进行。那么很显然一个简单的办法就是通过引用计数来判断一个对象是否可以被回收。不失一般性,如果一个对象没有任何引用与之关联,则说明该对象基本不太可能在其他地方被使用到,那么这个对象就成为可被回收的对象了。这种方式成为引用计数法。 这种方式的特点是实现简单,而且效率较高,但是它无法解决循环引用的问题,因此在Java中并没有采用这种方式(Python采用的是引用计数法)。看下面这段代码: 1 public class Main { 2 public static void main(String[] args) { 3 MyObject object1 = new MyObject(); 4 MyObject object2 = new MyObject(); 5 6 object1.object = object2; 7 object2.object = object1; 8 9 object1 = null; 10 object2 = null; 11 } 12 } 13 14 class MyObject{ 15 public Object object = null; 16 } 最后面两句将object1和object2赋值为null,也就是说object1和object2指向的对象已经不可能再被访问,但是由于它们互相引用对方,导致它们的引用计数都不为0,那么垃圾收集器就永远不会回收它们。 为了解决这个问题,在Java中采取了 可达性分析法。该方法的基本思想是通过一系列的“GC Roots”对象作为起点进行搜索,如果在“GC Roots”和一个对象之间没有可达路径,则称该对象是不可达的,不过要注意的是被判定为不可达的对象不一定就会成为可回收对象。被判定为不可达的对象要成为可回收对象必须至少经历两次标记过程,如果在这两次标记过程中仍然没有逃脱成为可回收对象的可能性,则基本上就真的成为可回收对象了。 至于可达性分析法具体是如何操作的我暂时也没有看得很明白,如果有哪位朋友比较清楚的话请不吝指教。 下面来看个例子: 1 Object aobj = new Object ( ) ; 2 Object bobj = new Object ( ) ; 3 Object cobj = new Object ( ) ; 4 aobj = bobj; 5 aobj = cobj; 6 cobj = null; 7 aobj = null; 第几行有可能会使得某个对象成为可回收对象?第7行的代码会导致有对象会成为可回收对象。至于为什么留给读者自己思考。 再看一个例子: 1 String str = new String("hello"); 2 SoftReference<String> sr = new SoftReference<String>(new String("java")); 3 WeakReference<String> wr = new WeakReference<String>(new String("world")); 这三句哪句会使得String对象成为可回收对象?第2句和第3句,第2句在内存不足的情况下会将String对象判定为可回收对象,第3句无论什么情况下String对象都会被判定为可回收对象。 最后总结一下平常遇到的比较常见的将对象判定为可回收对象的情况: 1)显示地将某个引用赋值为null或者将已经指向某个对象的引用指向新的对象,比如下面的代码: 1 Object obj = new Object(); 2 obj = null; 3 Object obj1 = new Object(); 4 Object obj2 = new Object(); 5 obj1 = obj2; 2)局部引用所指向的对象,比如下面这段代码: 1 void fun() { 2 3 ..... 4 for(int i=0;i<10;i++) { 5 Object obj = new Object(); 6 System.out.println(obj.getClass()); 7 } 8 } 循环每执行完一次,生成的Object对象都会成为可回收的对象。 3)只有弱引用与其关联的对象,比如: WeakReference<String> wr = new WeakReference<String>(new String("world")); 二.典型的垃圾收集算法 在确定了哪些垃圾可以被回收后,垃圾收集器要做的事情就是开始进行垃圾回收,但是这里面涉及到一个问题是:如何高效地进行垃圾回收。由于Java虚拟机规范并没有对如何实现垃圾收集器做出明确的规定,因此各个厂商的虚拟机可以采用不同的方式来实现垃圾收集器,所以在此只讨论几种常见的垃圾收集算法的核心思想。 1.Mark-Sweep(标记-清除)算法 这是最基础的垃圾回收算法,之所以说它是最基础的是因为它最容易实现,思想也是最简单的。标记-清除算法分为两个阶段:标记阶段和清除阶段。标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。具体过程如下图所示: 从图中可以很容易看出标记-清除算法实现起来比较容易,但是有一个比较严重的问题就是容易产生内存碎片,碎片太多可能会导致后续过程中需要为大对象分配空间时无法找到足够的空间而提前触发新的一次垃圾收集动作。 2.Copying(复制)算法 为了解决Mark-Sweep算法的缺陷,Copying算法就被提了出来。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。具体过程如下图所示: 这种算法虽然实现简单,运行高效且不容易产生内存碎片,但是却对内存空间的使用做出了高昂的代价,因为能够使用的内存缩减到原来的一半。 很显然,Copying算法的效率跟存活对象的数目多少有很大的关系,如果存活对象很多,那么Copying算法的效率将会大大降低。 3.Mark-Compact(标记-整理)算法 为了解决Copying算法的缺陷,充分利用内存空间,提出了Mark-Compact算法。该算法标记阶段和Mark-Sweep一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。具体过程如下图所示: 4.Generational Collection(分代收集)算法 分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。 目前大部分垃圾收集器对于新生代都采取Copying算法,因为新生代中每次垃圾回收都要回收大部分对象,也就是说需要复制的操作次数较少,但是实际中并不是按照1:1的比例来划分新生代的空间的,一般来说是将新生代划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另一块Survivor空间中,然后清理掉Eden和刚才使用过的Survivor空间。 而由于老年代的特点是每次回收都只回收少量对象,一般使用的是Mark-Compact算法。 注意,在堆区之外还有一个代就是永久代(Permanet Generation),它用来存储class类、常量、方法描述等。对永久代的回收主要回收两部分内容:废弃常量和无用的类。 三.典型的垃圾收集器 垃圾收集算法是 内存回收的理论基础,而垃圾收集器就是内存回收的具体实现。下面介绍一下HotSpot(JDK 7)虚拟机提供的几种垃圾收集器,用户可以根据自己的需求组合出各个年代使用的收集器。 1.Serial/Serial Old Serial/Serial Old收集器是最基本最古老的收集器,它是一个单线程收集器,并且在它进行垃圾收集时,必须暂停所有用户线程。Serial收集器是针对新生代的收集器,采用的是Copying算法,Serial Old收集器是针对老年代的收集器,采用的是Mark-Compact算法。它的优点是实现简单高效,但是缺点是会给用户带来停顿。 2.ParNew ParNew收集器是Serial收集器的多线程版本,使用多个线程进行垃圾收集。 3.Parallel Scavenge Parallel Scavenge收集器是一个新生代的多线程收集器(并行收集器),它在回收期间不需要暂停其他用户线程,其采用的是Copying算法,该收集器与前两个收集器有所不同,它主要是为了达到一个可控的吞吐量。 4.Parallel Old Parallel Old是Parallel Scavenge收集器的老年代版本(并行收集器),使用多线程和Mark-Compact算法。 5.CMS CMS(Current Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,它是一种并发收集器,采用的是Mark-Sweep算法。 6.G1 G1收集器是当今收集器技术发展最前沿的成果,它是一款面向服务端应用的收集器,它能充分利用多CPU、多核环境。因此它是一款并行与并发收集器,并且它能建立可预测的停顿时间模型。 下面补充一下关于内存分配方面的东西: 对象的内存分配,往大方向上讲就是在堆上分配,对象主要分配在新生代的Eden Space和From Space,少数情况下会直接分配在老年代。如果新生代的Eden Space和From Space的空间不足,则会发起一次GC,如果进行了GC之后,Eden Space和From Space能够容纳该对象就放在Eden Space和From Space。在GC的过程中,会将Eden Space和From Space中的存活对象移动到To Space,然后将Eden Space和From Space进行清理。如果在清理的过程中,To Space无法足够来存储某个对象,就会将该对象移动到老年代中。在进行了GC之后,使用的便是Eden space和To Space了,下次GC时会将存活对象复制到From Space,如此反复循环。当对象在Survivor区躲过一次GC的话,其对象年龄便会加1,默认情况下,如果对象年龄达到15岁,就会移动到老年代中。 一般来说,大对象会被直接分配到老年代,所谓的大对象是指需要大量连续存储空间的对象,最常见的一种大对象就是大数组,比如: byte[] data = new byte[4*1024*1024] 这种一般会直接在老年代分配存储空间。 当然分配的规则并不是百分之百固定的,这要取决于当前使用的是哪种垃圾收集器组合和JVM的相关参数。 参考资料: 《深入理解Java虚拟机》 作者: 海子 出处: http://www.cnblogs.com/dolphin0520/ 本博客中未标明转载的文章归作者 海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 小舟从此逝,江海寄余生。 --狐狸

优秀的个人博客,低调大师

浅谈OceanBase的锁机制原理

OceanBase锁定粒度为行锁,默认情况下的隔离级别为读取已提交(read committed)。另外,读操作总是读取某个版本的快照数据,不需要加锁。 只写事务(修改单行):事务预提交时对待修改的数据行加写锁,事务提交时释放写锁。 只写事务(修改多行):事务预提交时对待修改的多个数据行加写锁,事务提交时释放写锁。为了保证一致性,采用两阶段镇的方式实现,即需要在事务预提交阶段获取所有数据行的写锁,如果获取某行写锁失败,整个事务执行失败。 读写事务(read commited):读写事务中的读操作读取某个版本的快照,写操作的加锁方式与只写事务相同。 为了保证系统并发性能,OceanBase暂时不支持更高的隔离级别。另外,为了支持对一致性要求很高的业务,OceanBase允许用户显式锁住某个数据行。例如,有一张账务表account(account_id,balance),其中account_id为主键。假设需要从A账户(account_id=1)向B账户(account_id=2)转账100元,那么,A账户需要减少100元,B账户需要增加100元,整个转账操作是一个事务,执行过程中需要防止A账户和B账户被其他事务并发修改。 如以下代码所示,OceanBase提供了”select...for update”语句用于显示锁住A账户或者B账户,防止转账过程中被其他事务并发修改。 select balance as balance_a from account where account id=1 for update; //锁住A账户 select balance as balance_b from account where account_id=2 for update; //锁住B账户 事务执行过程中可能会发生死锁,例如事务T1持有账户A的写锁并尝试获取账户B的写锁,事务T2持有账户B的写锁并尝试获取账户A的写锁,这两个事务因为循环等待而出现死锁。OceanBase目前处理死锁的方式很简单,事务执行过程中如果超过一定时间无法获取写锁,则自动回滚。

优秀的个人博客,低调大师

YARN ResourceManager重启作业保留机制

1. 前置 YARN可以通过相关配置支持ResourceManager重启过程中,不影响正在运行的作业,即重启后,作业还能正常继续运行直到结束。 参见详细文档ResourceManagerRestart 涉及配置如下: key value 备注 yarn.resourcemanager.recovery.enabled true yarn.resourcemanager.store.class org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore 有三种StateStore,分别是基于zookeeper, HDFS, leveldb, HA高可用集群必须用ZKRMStateStore yarn.resourcemanager.work-preserving-recove

优秀的个人博客,低调大师

Linux 软中断机制分析zz

http://blog.jobbole.com/107057/ 1. 为什么要软中断 编写驱动的时候,一个中断产生之后,内核在中断处理函数中可能需要完成很多工作。但是中断处理函数的处理是关闭了中断的。也就是说在响应中断时,系统不能再次响应外部的其它中断。这样的后果会造成有可能丢失外部中断。于是,linux内核设计出了一种架构,中断函数需要处理的任务分为两部分,一部分在中断处理函数中执行,这时系统关闭中断。另外一部分在软件中断中执行,这个时候开启中断,系统可以响应外部中断。 关于软件中断的理论各种书籍都有介绍,不多叙述。而要真正体会软件中断的作用就必须从代码的角度来分析。我们做工作时候讲求的是professional,当一个人在某个领域一无所知的时候,我们称他为小白,偶,非苹果电脑。小白的脑子里充满了各种问题。慢慢的当这些疑惑解释完之后,小白就脱白了。此时,我们对这个领域的基本框架有了解,但这和professional还有一定的差距。再加以时日,逐渐融会贯通该领域才能达到专业的境界。 2. 什么时候触发处理软件中断 说了这么多废话,赶快步入正题。初识软中断,脑子里肯定有不少的疑问,首先就是软件中断在什么地方被触发处理?这个问题的答案就是:一个硬件中断处理完成之后。下面的函数在处理完硬件中断之后推出中断处理函数,在irq_exit中会触发软件中断的处理。 asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs) { struct pt_regs *old_regs = set_irq_regs(regs);irq_enter(); /* * Some hardware gives randomly wrong interrupts. Rather * than crashing, do something sensible. */ if (irq >= NR_IRQS) handle_bad_irq(irq, &bad_irq_desc); else generic_handle_irq(irq); /* AT91 specific workaround */ irq_finish(irq); irq_exit(); set_irq_regs(old_regs); } 这里要注意,invoke_softirq必须满足两个条件才能被调用到,一个就是不是在硬件中断处理过程中或者在软件中断处理中,第二个就是必须有软件中断处于pending状态。第二个好理解,有软件中断产生才去处理,没有就不处理。第一个就不好理解了。 /* * Exit an interrupt context. Process softirqs if needed and possible: */ void irq_exit(void) { account_system_vtime(current); trace_hardirq_exit(); sub_preempt_count(IRQ_EXIT_OFFSET); if (!in_interrupt() && local_softirq_pending()) invoke_softirq();#ifdef CONFIG_NO_HZ /* Make sure that timer wheel updates are propagated */ rcu_irq_exit(); if (idle_cpu(smp_processor_id()) && !in_interrupt() && !need_resched()) tick_nohz_stop_sched_tick(0); #endif preempt_enable_no_resched(); } 在linux系统的进程数据结构里,有这么一个数据结构 #define preempt_count()(current_thread_info()->preempt_count), 利用preempt_count可以表示是否处于中断处理或者软件中断处理过程中。 #define PREEMPT_MASK (__IRQ_MASK(PREEMPT_BITS) #define SOFTIRQ_MASK (__IRQ_MASK(SOFTIRQ_BITS) #define HARDIRQ_MASK (__IRQ_MASK(HARDIRQ_BITS)#define PREEMPT_OFFSET (1UL #define SOFTIRQ_OFFSET (1UL #define HARDIRQ_OFFSET (1UL sub_preempt_count(IRQ_EXIT_OFFSET); #define in_interrupt()(irq_count()) #define irq_count()(preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) preempt_count的8~23位记录中断处理和软件中断处理过程的计数。如果有计数,表示系统在硬件中断或者软件中断处理过程中。系统这么设计是为了避免软件中断在中断嵌套中被调用,并且达到在单个CPU上软件中断不能被重入的目的。对于ARM架构的CPU不存在中断嵌套中调用软件中断的问题,因为ARM架构的CPU在处理硬件中断的过程中是关闭掉中断的。只有在进入了软中断处理过程中之后才会开启硬件中断,如果在软件中断处理过程中有硬件中断嵌套,也不会再次调用软中断,because硬件中断是软件中断处理过程中再次进入的,此时preempt_count已经记录了软件中断!对于其它架构的CPU,有可能在触发调用软件中断前,也就是还在处理硬件中断的时候,就已经开启了硬件中断,可能会发生中断嵌套,在中断嵌套中是不允许调用软件中断处理的。Why?我的理解是,在发生中断嵌套的时候,表明这个时候是系统突发繁忙的时候,内核第一要务就是赶紧把中断中的事情处理完成,退出中断嵌套。避免多次嵌套,哪里有时间处理软件中断,所以把软件中断推迟到了所有中断处理完成的时候才能触发软件中断。 3. 软件中断的处理过程 之前我已经说到,软中断的一个很大的目的就是避免中断处理中,处理的操作过多而丢失中断。同时中断还需要考虑到一件事情就是中断处理过程过长就会影响系统响应时间。如果一个中断处理一秒钟,那你一定能感受到串口卡住的现象。从另外一方面说呢,我们又必须考虑中断处理的操作一定的优先度,毕竟是硬件触发的事务,关系到网络、块设备的效率问题。Linux内核就中断方面就必须考虑平衡这三个方面的问题。而下面我要分析的__do_softirq函数就恰似在这三者之间打太极,游刃有余,面面俱到! /* * We restart softirq processing MAX_SOFTIRQ_RESTART times, * and we fall back to softirqd after that. * * This number has been established via experimentation. * The two things to balance is latency against fairness - * we want to handle softirqs as soon as possible, but they * should not be able to lock up the box. */ #define MAX_SOFTIRQ_RESTART 10asmlinkage void __do_softirq(void) { struct softirq_action *h; __u32 pending; int max_restart = MAX_SOFTIRQ_RESTART; int cpu;pending = local_softirq_pending(); account_system_vtime(current);__local_bh_disable((unsigned long)__builtin_return_address(0)); trace_softirq_enter(); cpu = smp_processor_id(); restart: /* Reset the pending bitmask before enabling irqs */ set_softirq_pending(0); local_irq_enable(); h = softirq_vec; do { if (pending & 1) { int prev_count = preempt_count(); h->action(h); if (unlikely(prev_count != preempt_count())) { printk(KERN_ERR "huh, entered softirq %td %p" "with preempt_count %08x," " exited with %08x?n", h - softirq_vec, h->action, prev_count, preempt_count()); preempt_count() = prev_count; } rcu_bh_qsctr_inc(cpu); } h++; pending >>= 1; } while (pending); local_irq_disable(); pending = local_softirq_pending(); if (pending && --max_restart) goto restart; if (pending) wakeup_softirqd(); trace_softirq_exit(); account_system_vtime(current); _local_bh_enable(); } __do_softirq函数处理软件中断过程如下图流程分析 4. 首先调用local_softirq_pending函数取得目前有哪些位存在软件中断 5. 调用__local_bh_disable关闭软中断,其实就是设置正在处理软件中断标记,在同一个CPU上使得不能重入__do_softirq函数 6. 重新设置软中断标记为0,set_softirq_pending重新设置软中断标记为0,这样在之后重新开启中断之后硬件中断中又可以设置软件中断位。 7. 开启硬件中断 8. 之后在一个循环中,遍历pending标志的每一位,如果这一位设置就会调用软件中断的处理函数。在这个过程中硬件中断是开启的,随时可以打断软件中断。这样保证硬件中断不会丢失。 9. 之后关闭硬件中断,查看是否又有软件中断处于pending状态,如果是,并且在本次调用__do_softirq函数过程中没有累计重复进入软件中断处理的次数超过10次,就可以重新调用软件中断处理。如果超过了10次,就调用wakeup_softirqd();唤醒内核的一个进程来处理软件中断。设立10次的限制,也是为了避免影响系统响应时间。 4. 处理软中断内核线程 之前我说到不能让CPU长时间来处理中断事务,这样会影响系统的响应时间,严重影响用户和系统之间的交互式体验。所以在之前的__do_softirq中最多将循环执行10次,那么当执行了10次仍然有软中断在pending状态,这个时候应该怎么处理呢?系统将唤醒一个软件中断处理的内核进程,在内核进程中处理pending中的软件中断。这里要注意,之前我们分析的触发软件中断的位置其实是中断上下文中,而在软中断的内核线程中实际已经是进程的上下文。 这里说的软中断上下文指的就是系统为每个CPU建立的ksoftirqd进程。 看完这个函数,我不得不佩服这个函数设计的精巧!而我更多的从中体会到其中蕴藏的一种做人的道理。那就是做人要霸道一点,太谦和太恭维不行,但是又不能横行霸道,原则的问题要公平讲理,一定的时候顾及别人的利益,好处不能一个人独吞。这就跟下面ksoftirqd处理过程一样,该狠的时候禁止抢占,其它进程别想调度到哦,但是自己占用CPU时间过长的话,也自觉的问一问是不是该释放CPU给其它进程了。 下面我们就来分析一下这个处理过程怎么就体现了上面的这种说法呢?软中断的内核进程中主要有两个大循环,外层的循环处理有软件中断就处理,没有软件中断就休眠。内层的循环处理软件中断,并每循环一次都试探一次是否过长时间占据了CPU,需要调度释放CPU给其它进程。具体的操作在注释中做了解释。 static int ksoftirqd(void *__bind_cpu) { set_current_state(TASK_INTERRUPTIBLE);while (!kthread_should_stop()) { /*不管三七二十一首先禁止抢占,我掌握CPU,并全凭我自己掌握调度*/ preempt_disable(); if (!local_softirq_pending()) { preempt_enable_no_resched(); /*如果没有软中断在pending,那就让出CPU来吧*/ schedule(); /*我被唤醒了,首先掌握CPU,不让自己被抢占,自己决定自己的是否要调度*/ preempt_disable(); }__set_current_state(TASK_RUNNING);while (local_softirq_pending()) { /* Preempt disable stops cpu going offline. If already offline, we'll be on wrong CPU: don't process */ if (cpu_is_offline((long)__bind_cpu)) goto wait_to_die; /*处理软中断*/ do_softirq(); /*虽然我自己掌握是否要调度,虽然我可以一直不调度,但是我是 个正直的人,运行一段时间后我会看看是否需要调度,还其它进程运行*/ preempt_enable_no_resched(); cond_resched(); preempt_disable(); rcu_qsctr_inc((long)__bind_cpu); } preempt_enable(); set_current_state(TASK_INTERRUPTIBLE); } __set_current_state(TASK_RUNNING); return 0; wait_to_die: preempt_enable(); /* Wait for kthread_stop */ set_current_state(TASK_INTERRUPTIBLE); while (!kthread_should_stop()) { schedule(); set_current_state(TASK_INTERRUPTIBLE); } __set_current_state(TASK_RUNNING); return 0; }

优秀的个人博客,低调大师

java代理模式与反射机制

代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。 代理模式一般涉及到的角色有: 抽象角色:声明真实对象和代理对象的共同接口; 代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。 真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。 一个简单的例子如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package com.java.proxy; interface Subject{ abstract public void request(); } class RealSubject implements Subject{ @Override public void request() { System.out.println( "real subject" ); } } class ProxySubject implements Subject{ Subject subject; public ProxySubject() { } public ProxySubject(Subject subject){ this .subject=subject; } @Override public void request() { System.out.println( "预处理工作" ); subject.request(); System.out.println( "后续工作" ); } } public class ProxyModel { public static void main(String[] args) { Subject subject= new ProxySubject( new RealSubject()); subject.request(); } } 由以上代码可以看出,客户实际需要调用的是RealSubject类的request()方法,现在用ProxySubject来代理 RealSubject类,同样达到目的,同时还可以封装其他方法(可以做一些预处理,和后续工作),可以处理一些其他问题。 另外,如果要按照上述的方法使用代理模式,那么真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须 对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色,该如何使用代理呢?这个问题可以通过Java的动态代理类来解决。 动态代理 Java动态代理类位于Java.lang.reflect包下,一般主要涉及到以下两个类: (1).Interface InvocationHandler:该接口中仅定义了一个方法Object:invoke(Object obj,Method method, Object[] args)。在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。 这个抽象方法在代理类中动态实现。 (2).Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含以下内容: Protected Proxy(InvocationHandler h):构造函数,估计用于给内部的h赋值。 Static Class getProxyClass (ClassLoader loader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。 Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)。 所谓Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然啦,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作 下面我们使用动态代理来改写上面的例子: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package com.java.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; interface Subject{ abstract public void request(); } class RealSubject implements Subject{ public RealSubject() { } @Override public void request() { System.out.println( "real subject" ); } } class ProxySubject implements InvocationHandler{ private Subject obj; public ProxySubject() { } public ProxySubject(Subject obj){ this .obj=obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println( "预处理工作" ); method.invoke(obj, args); System.out.println( "后续工作" ); return null ; } } public class ProxyModel { public static void main(String[] args) { RealSubject realSubject= new RealSubject(); Class<?>cla=realSubject.getClass(); InvocationHandler handler= new ProxySubject(realSubject); Subject subject=(Subject)Proxy.newProxyInstance(cla.getClassLoader(), cla.getInterfaces(), handler); subject.request(); } } 最后我们给出一个在远程方法中使用代理的例子:因为代码很长,所以不打算在文章中粘贴代码了,我把项目上传到微盘了,下载地址为:http://vdisk.weibo.com/s/jax8z 项目的部署为: ============================================================================== 本文转自被遗忘的博客园博客,原文链接:http://www.cnblogs.com/rollenholt/archive/2012/11/28/2792779.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

iOS开发-沙盒(sandbox)机制

苹果前天发的财报,貌似现在用ios系统的比以前又多了一些,但是大家的iPhone购买的渠道也是五花八门,有的从非正规渠道购买的iPhone里的操作系统已经被越狱过,越狱这个事情和Android的root刷机是是一样的,正常情况下大家用手机都是没问题的。不过有些人总是觉得iOS和Android各种用的不爽,需要管理员权限,不过凡事有利有弊,以前有人买苹果刷机是因为输入法。现在输入法问题解决了,还是有人越狱,花几千大洋买个iPhone然后越狱,这种事情想想都很。。 Android系统中应用程序安装后,系统会为其分配一个独立的存储空间(Security Sandbox),存放字节码文件、资源文件及配置文件,同时为应用程序分配唯一的ID,标识应用程序的相关文件和资源,系统通过设置权限从而实现一个应用程序在一般情况下只能访问该应用程序的文件和资源。iOS操作系统差不多类似,iOS是从苹果的next操作系统发展过来的,next是一款类unix操作系统,unix系统对于文件权限的限制非常严格,正常情况下普通用户常用的操作根本用不到root权限,用户的操作请求假如是已分配权限,自然可以执行,反之需要root权限的话就不被允许。对于应用程序而言也是同样的,分配一个空间,然后只有允许的权限才能访问内容。 下面是张苹果官网以前关于沙盒方面的图片: 上面这个图就是常说的每个应用程序都有自己的存储空间,应用程序不能翻过自己的围墙去访问别的存储空间的内容,应用程序请求的数据都要通过权限检测,假如不符合条件的话,不会被放行。其实简单理解每个应用都是其独立的分配空间,比如说如果360可以访问QQ聊天的内容,那么估计Pony就破产了~ sandbox的启用流程:①sandbox_init 通过libsandbox.dylib转换为二进制传递给kernelmac syscallTrustedBSD ②将sandbox_init 的请求发sandbox.kext 的扩展(看到这里会想到IIS扩展), 扩展为当前进程安装证书,然后返回安装结果.③如果安装成功,每次进程的请求,都会被trusted bsd 发送给sandbox.kext 去匹配之前安装的证书规则。如果证书不匹配的话,则安装失败。 为了看下xCode模拟器中启动的Demo路径和类型,可以进行接下来的操作,在 viewDidLoad加一句话: 1 NSLog (@ "路径:%@" , NSHomeDirectory ()); 这个时候会看到一个路径,测试生成结果: 1 路径:/Users/用户名/Library/Developer/CoreSimulator/Devices/C53A5D69-DEDD-4598-9297-79B811E14DAD/data/Containers/Data/Application/0FCDA696-03B4-4D8D-B99B-E94D1F843561 打开Finder是没法找到这个路径的,需要在终端中输入命令显示隐藏文件: 终端中显示隐藏文件的命令:defaults write com.apple.finder AppleShowAllFiles -bool true 终端中不显示隐藏文件的命令:defaults write com.apple.finder AppleShowAllFiles -bool false 模拟器的信息在CoreSimulator中: 路径比较长,之后是模拟器型号,模拟器的具体信息: 之后在Data中可以看到具体应用信息,和一个应用包括的目录信息: Documents目录存储的是程序中创建的或在程序中浏览到的文件数据,Library存储程序的默认设置或其它 状态信息;Library下的Caches目录存放缓存文件,Library下的Preferences目录存放的应用程序的偏好设置,tmp保存的是创建和存放的临时文件,以上路径也可以通过程序获取路径viewDidLoad中加入以下代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 //获取Documents文件夹目录,NSDocumentDirectory获取Doucments文件夹目录,NSUserDomainMask是在当前应用沙盒中获取,所有应用沙盒目录组成一个数组结构的数据存放 NSArray *documentPath = NSSearchPathForDirectoriesInDomains ( NSDocumentDirectory , NSUserDomainMask , YES ); NSLog (@ "Documents目录:%@" ,[documentPath objectAtIndex:0]); //缓存Cache目录 NSArray *cachePath = NSSearchPathForDirectoriesInDomains ( NSCachesDirectory , NSUserDomainMask , YES ); NSLog (@ "缓存目录:%@" ,[cachePath objectAtIndex:0]); //Library目录 NSArray *domainPath = NSSearchPathForDirectoriesInDomains ( NSLibraryDirectory , NSUserDomainMask , YES ); NSLog (@ "资源库目录:%@" ,[domainPath objectAtIndex:0]); //临时文件temp目录 NSLog (@ "临时文件目录:%@" , NSTemporaryDirectory ()); 本文转自Fly_Elephant博客园博客,原文链接:http://www.cnblogs.com/xiaofeixiang/p/4257394.html,如需转载请自行联系原作者

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册