精华内容
下载资源
问答
  • java对象引入四种情况详解

    千次阅读 2017-04-18 15:38:41
    在jdk 1.2以前,创建的对象只有处在可触及(reachaable)状态下,才能被程序所以使用,垃圾回收器一旦发现无用对象,便会对其进行回收。但是,在某些情况下,我们希望有些对象不需要立刻回收或者说从全局的角度来说...

    在jdk 1.2以前,创建的对象只有处在可触及(reachaable)状态下,才能被程序所以使用,垃圾回收器一旦发现无用对象,便会对其进行回收。但是,在某些情况下,我们希望有些对象不需要立刻回收或者说从全局的角度来说并没有立刻回收的必要性。比如缓存系统的设计,在内存不吃紧或者说为了提高运行效率的情况下,一些暂时不用的对象仍然可放置在内存中,而不是立刻进行回收。因此,从jdk 1.2 版本开始,Java设计人员把对象的引用细分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)四种级别,主要区别体现在在被GC回收的优先级上:强引用->软引用->弱引用->虚引用。也就是说从jdk 1.2开始,垃圾回收器回收对象时,对象的可达性分析需要考虑考虑对象的引用强度,也就是说现在对象的有效性=可达性+引用类型。先来看下类层次结构如下:


    1. 引用的四种类型

    1. 强引用(Strong Reference)

    在代码中普遍使用的,类似Person person=new Person();如果一个对象具有强引用,则无论在什么情况下,GC都不会回收被引用的对象。当内存空间不足时,JAVA虚拟机宁可抛出OutOfMemoryError终止应用程序也不会回收具有强引用的对象。

    2. 软引用(Soft Reference)

    表示一个对象处在有用但非必须的状态。如果一个对象具有软引用,在内存空间充足时,GC就不会回收该对象;当内存空间不足时,GC会回收该对象的内存(回收发生在OutOfMemoryError之前)。

    Person person=new Person();
    SoftReference sr=new SoftReference(person);
    • 1
    • 2
    • 1
    • 2

    软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被GC回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中,以便在恰当的时候将该软引用回收。但是由于GC线程的优先级较低,通常手动调用System.gc()并不能立即执行GC,因此弱引用所引用的对象并不一定会被马上回收。

    3. 弱引用(Weak Reference)

    用来描述非必须的对象。它类似软引用,但是强度比软引用更弱一些:弱引用具有更短的生命.GC在扫描的过程中,一旦发现只具有被弱引用关联的对象,都会回收掉被弱引用关联的对象。换言之,无论当前内存是否紧缺,GC都将回收被弱引用关联的对象。

    Person person=new Person();
    WeakReference wr=new WeakReference(person);
    • 1
    • 2
    • 1
    • 2

    同样弱引用也可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被GC回收了,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中,以便在恰当的时候将该弱引用回收。

    4. 虚引用(Phantom Reference)

    虚引等同于没有引用,这意味着在任何时候都可能被GC回收,设置虚引用的目的是为了被虚引用关联的对象在被垃圾回收器回收时,能够收到一个系统通知。(被用来跟踪对象被GC回收的活动)虚引用和弱引用的区别在于:虚引用在使用时必须和引用队列(ReferenceQueue)联合使用,其在GC回收期间的活动如下:

    ReferenceQueue queue=new ReferenceQueue();
    PhantomReference pr=new PhantomReference(object.queue);
    • 1
    • 2
    • 1
    • 2

    也即是GC在回收一个对象时,如果发现该对象具有虚引用,那么在回收之前会首先该对象的虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入虚引用来了解被引用的对象是否被GC回收。


    ReferenceQueue和Reference

    1. ReferenceQueue含义及作用

    通常我们将其ReferenceQueue翻译为引用队列,换言之就是存放引用的队列,保存的是Reference对象。其作用在于Reference对象所引用的对象被GC回收时,该Reference对象将会被加入引用队列中(ReferenceQueue)的队列末尾,这相当于是一种通知机制.当关联的引用队列中有数据的时候,意味着引用指向的堆内存中的对象被回收。通过这种方式,JVM允许我们在对象被销毁后,做一些我们自己想做的事情。JVM提供了一个ReferenceHandler线程,将引用加入到注册的引用队列中

    ReferenceQueue常用的方法: 
    public Reference<? extends T> poll():从队列中取出一个元素,队列为空则返回null; 
    public Reference<? extends T> remove():从队列中出对一个元素,若没有则阻塞至有可出队元素; 
    public Reference<? extends T> remove(long timeout):从队列中出对一个元素,若没有则阻塞至有可出对元素或阻塞至超过timeout毫秒;

    见如下代码:

    ReferenceQueue< Person> rq=new ReferenceQueue<Person>();
    Person person=new Person();
    SoftReference sr=new SoftReference(person,rq);
    • 1
    • 2
    • 3
    • 1
    • 2
    • 3

    这段代码中,对于Person对象有两种引用类型,一是person的强引用,而是sr的软引用。sr强引用了SoftReference对象,该对象软引用了Person对象。当person被回收时,sr所强引用的对象将会被放到rq的队列末尾。利用ReferenceQueue可以清除失去了软引用对象的SoftReference,如下操作:

    SoftReference ref=null;
    while((ref=(Person)rq.poll())!=null){
        //清除
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 1
    • 2
    • 3
    • 4
    • 5

    2. Reference类

    Reference是SoftReference,WeakReference,PhantomReference类的父类,其内部通过一个next字段来构建了一个Reference类型的单向列表,而queue字段存放了引用对象对应的引用队列,若在Reference的子类构造函数中没有指定,则默认关联一个ReferenceQueue.NULL队列。


    3. 四种引用类型使用场景

    强引用类型是在代码中普遍存在,无须解释太多了 
    软引用和弱引用:两者都可以实现缓存功能,但软引用实现的缓存通常用在服务端,而在移动设备中的内存更为紧缺,对垃圾回收更为敏感,因此Android中的缓存通常是用弱引用来实现(比如LruCache) 
    在开发中有这么一个场景:用户信息查询。在不考虑对用户信息更改的情况下,通常有以下两种方案来实现: 
    1. 每次查询时,连接数据库获取信息,缺点是IO读频繁,平均响应时间较长。优点是内存占用少。 
    2. 第一次查询时,读取数据库后将用户信息存放在内存中,以后每次查询从内存中读取,优点是读取速度快,在请求次数较少的情况下,内存占用较多。 
    现在我们用采用第二种方案,基于缓存来设计,代码如下:

    User.java

    public class User {
    
        private String id;
        private String name;
        private int age;
        public User(String id) {
            super();
            this.id = id;
        }
        public String getId() {
            return id;
        }
        public void setId(String id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        @Override
        public String toString() {
            return "User [id=" + id + ", name=" + name + ", age=" + age + "]";
        }
    
    • 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
    • 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

    UserCache.java

    
    public class UserCache {
        private static UserCache cache;
        private Hashtable<String, UserRef> userRefs;
        private ReferenceQueue<User> q;
    
        private class UserRef extends SoftReference<User>{
            private String key="";
            public UserRef(User user,ReferenceQueue<User> q){
                super(user,q);
                key=user.getId();
            }
        }
    
    
        private UserCache(){
            userRefs=new Hashtable<>();
            q=new ReferenceQueue<>();
        }
    
        public static UserCache getInstance(){
            synchronized (UserCache.class) {
                if(cache==null){
                    synchronized (UserCache.class) {
                        cache=new UserCache();
                    }
                }
    
            }
            return cache;
        }
    
        /**
         * 缓存用户数据
         * @param user
         */
        private void cacheObject(User user){
            cleanCache();
            UserRef ref = new UserRef(user, q);
            userRefs.put(user.getId(), ref);
        }
    
        /**
         * 获取用户数据
         * @param id
         * @return
         */
        public User getObject(String id){
            User user=null;
            if(userRefs.containsKey(id)){
                System.err.println("get data from cache");
                UserRef ref = userRefs.get(id);
                user=ref.get();
            }
            if(user==null){
                System.err.println("get data from db");
                user=new User(id);
                user.setName("dong"+new Random().nextInt(10));
                cacheObject(user);
            }
            return user;
        }
    
        private void cleanCache() {
            UserRef ref=null;
            while((ref=(UserRef) q.poll())!=null){
                userRefs.remove(ref.key);
            }
    
        }
    
        public void clearAll(){
            cleanCache();
            userRefs.clear();
            System.gc();
            System.runFinalization();
        }
    
    }
    
    • 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
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 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
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80

    UserCacheTest.java

    public class UserCacheTest {
    
        public static void main(String[] args) {
            UserCache cache = UserCache.getInstance();
    
            for(int i=0;i<2;i++){
                System.out.println(cache.getObject("123").toString());
            }
            cache.clearAll();
            System.out.println(cache.getObject("123").toString());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    这样简单的缓存就完成了,输出结果如下:

    get data from db 
    get data from cache 
    User [id=123, name=dong3, age=0] 
    User [id=123, name=dong3, age=0] 
    get data from db 
    User [id=123, name=dong2, age=0]


    4. 到底是什么引用类型?

    如果一个对象有多个引用类型,那在进行垃圾回收时如何判断对象的可达性呢?其原则如下:

    单挑引用链的可达性以最弱的一个引用类型来决定; 
    多条引用链的可达性以最强的一个引用类型来决定;

    举例说明,如下图所示: 
    引用类型判断 
    对Object 2进行分析,路径1-1——>2-1中取最弱的引用,软引用;路径1-2——>2-2中取最弱引用,虚引用;在这两条路径中取最强引用,软引用。因此Object 2是最终的引用类型是软引用。


    示例代码

    我们通过一段代码帮助理解这四种:

        private void test_gc1(){
            //在heap中创建内容为"wohenhao"的对象,并建立a到该对象的强引用,此时该对象时强可及
            String a=new String("wohenhao");
            //对heap中的对象建立软引用,此时heap中的对象仍然是强可及
            SoftReference< ?> softReference=new SoftReference<String>(a);
            //对heap中的对象建立弱引用,此时heap中的对象仍然是强可及
            WeakReference< ?> weakReference=new WeakReference<String>(a);
            System.out.println("强引用:"+a+"\n软引用"+softReference.get()+"\n弱引用"+weakReference.get()+"\n");
            //heap中的对象从强可及到软可及
            a=null;
            System.out.println("强引用:"+a+"\n软引用"+softReference.get()+"\n弱引用"+weakReference.get()+"\n");
            softReference.clear();//heap中对象从软可及变成弱可及,此时调用System.gc(),
            System.gc();
            System.out.println("强引用:"+a+"\n软引用"+softReference.get()+"\n弱引用"+weakReference.get()+"\n");
        }
    
        private void test_gc2(){
        //在heap中创建内容为"wohenhao"的对象,并建立a到该对象的强引用,此时该对象时强可及
            String a=new String("wohenhao");
            //对heap中的对象建立软引用,此时heap中的对象仍然是强可及
            SoftReference< ?> softReference=new SoftReference<String>(a);
            //对heap中的对象建立弱引用,此时heap中的对象仍然是强可及
            WeakReference< ?> weakReference=new WeakReference<String>(a);
            System.out.println("强引用:"+a+"\n软引用"+softReference.get()+"\n弱引用"+weakReference.get()+"\n");
            a=null;//heap中的对象从强可及到软可及
            System.out.println("强引用:"+a+"\n软引用"+softReference.get()+"\n弱引用"+weakReference.get()+"\n");
            System.gc();
            System.out.println("强引用:"+a+"\n软引用"+softReference.get()+"\n弱引用"+weakReference.get()+"\n");
        }
    展开全文
  • lua中对象引用

    千次阅读 2015-05-24 14:26:29
    几次编写lua时。总是有同事遇到A中对象已经释放了。...要想实现A释放了B中A的值也释放了这时候需要用到引用。 setmetatable(t,{__mode="k"}); __mode 的值可以为"k","v","kv"   ~ ~第一段代码中可以看到内
    几次编写lua时。总是有同事遇到A中对象已经释放了。但B对象中A对象的值不是为空的。
    Lua的gc和Java的类似。只有当对象没有被引用时候才会释放这块内存。要想实现A释放了B中A的值也释放了这时候需要用到弱引用。

    setmetatable(t,{__mode="k"});

    __mode 的值可以为"k","v","kv"

     



    ~ ~第一段代码中可以看到内存中是有一个[key]保存了一个table。虽然b=nil了但是t中还是存在了这一个条目。
    ~ ~第二段代码中可以看到[key]不在拥有table了。
    展开全文
  • JVM对象引用与内存分配策略

    千次阅读 2016-05-31 20:46:41
    前两天对《深入理解虚拟机》一书做了个总结:《JVM理解...即就算两个对象相互引用,只要这两个对象没有引用链连接GC Roots,这两个对象都会被判定为可回收的对象!注意,这里是指被判定位可回收的对象,并不是说他们

    我的简书同步发布:JVM对象引用与内存分配策略

    前两天对《深入理解虚拟机》一书做了个总结:《JVM理解其实并不难! 》,今天针对对象引用和内存分配做个稍微深入的了解。

    关于引用

    《JVM理解其实并不难! 》一文中提到,JVM是通过可达性分析来判断对象是否需要被回收,这可以弥补引用计数法的不足。即就算两个对象相互引用,只要这两个对象没有引用链连接GC Roots,这两个对象都会被判定为可回收的对象!注意,这里是指被判定位可回收的对象,并不是说他们就一定会被回收!这相当于“标记”的过程,即标记这个对象为可以回收的对象。

    什么意思呢?既然被标记为可回收的对象,难道不就是要对他们回收吗?且继续往下看~

    JVM中真正将一个对象判死刑至少需要经历两次标记过程:

    第一个过程,就是我们所熟知的可达性分析。
    第二个过程,就是判断这个对象是否需要执行finalize()方法。

    第一个过程无需再述,我们看看第二个过程,什么叫判断这个对象是否需要执行finalize()方法呢?在学习Java时,我们都知道,如果我们重写Objectfinalize()方法时,在当前这个对象消亡之前会执行finalize()方法。然后我们就将一些资源在finalize()中释放。其实这种做法并不正确,至于为什么不正确,我们来看看finalize()方法在垃圾回收时是怎么触发的。

    如果一个对象被判定为有必要执行finalize()方法,那么这个对象会被放入F-Queue队列中,JVM中有一个优先级比较低的线程去执行这个队列中的对象的finalize()方法。这里的执行是指JVM会触发这个方法,但不会承诺会等到它运行结束,这主要是防止某个对象的finalize过于耗时(比如死循环),导致队列中其他的对象无法被执行,最终使得整个内存回收系统崩溃~

    那么什么样的对象会被判定为有必要执行finalize()方法呢?首先,这个对象必须是通过可达性分析判定为没有引用链连接到GC Roots;其次,这个对象重写了finalize()方法并且没有执行过finalize()方法。也就是说finalize()只会被执行一次。

    既然如此,我们可以写一个对象自救的测试,让一个对象自己拯救自己,我们去看看一个对象是如何“耍流氓”的:

    
    public class Test {
        public static Test self;
    
    
    
        @Override
        protected void finalize() throws Throwable {
            super.finalize();
            System.out.println("嗯,已经执行了finalize()方法了");
            Test.self = this;
        }
    
    
        private static void save() throws Exception {
    
            self = null;
            System.gc();
    
            //Finalizer线程优先级比较低,我们稍等一小会
            Thread.sleep(500);
    
            if (self != null) {
                System.out.println("我还活着~你咬我啊~");
            } else {
                System.out.println("啊哦,我挂了~");
            }
        }
    
        public static void main(String[] args) throws Exception {
            self = new Test();
    
            save();
            save();
    
        }
    }
    
    

    运行结果是:

    嗯,已经执行了finalize()方法了
    我还活着~你咬我啊~
    啊哦,我挂了~

    我们看到,同样是执行save方法,第一次对象成功拯救了自己,第二次却无法拯救自己,finalize方法也仅仅只被执行了一次而已!

    软引用、弱引用、虚引用

    我们知道,只有当对象存在引用链连接GC Roots时才确保不会被回收,即对象为强引用。那么有些对象,我们希望在内存充足的情况下不要回收,在内存不足的时候再将其回收掉。如果只有强引用,那这个对象永远都不会被回收。于是引入了软引用弱引用虚引用

    软引用:用来描述一些还有用但并非必须的对象。对于软引用对象,在系统将要发生内存溢出之前,会把这些对象列入回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才抛出溢出异常。

    怎么理解呢?其实就是说,如果我的内存不足了,JVM有点想骂人(抛异常)了,JVM会先看看哪些对象时软引用对象,先把这些软引用对象给回收掉,再看看内存是不是够用,如果还是不够用,那JVM就真的发飙了(抛异常)。

    弱引用:同样,弱引用也是描述非必须的对象,但是它的强度更弱一点,被弱引用关联的对象只能生存到下一次垃圾回收之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉弱引用的对象。

    有了软引用的理解基础,我们理解起来就不费劲了。就是说,弱引用对象能生存到下次垃圾回收,它比软引用活的时间要短。爱思考的你肯定会想问:那这跟直接将对象赋null值有什么区别啊,如果一个对象“切断”掉对对象的引用,那个对象也是活到下次垃圾回收啊。可是,有没有想过,假设引用变量A a=new A()。然后你直接a=null,那如果在a之前所引用的对象被回收之前,我还想引用它怎么办?你已经没有办法找到它了~~~~,但是弱引用就不一样,我们可以先判断a引用的对象有没有被回收即if(a!=null),如果没有被回收,我们就还可以利用它啦~

    虚引用:它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对这个对象的生死有影响。你无法通过虚引用来取得一个对象实例。为一个对象设置虚引用管理的唯一目的就是能在这个对象呗收集器回收时收到一个系统通知。

    我们来讨论一下,什么时候该用软引用,什么时候该用弱引用。首先,软引用比弱引用活的时间长一点,当你不希望某个对象轻易被回收,但是呢由于这个对象比较占用内存,为了防止OOM,你可以将它声明为软引用。那什么时候用弱引用呢?当某个对象,你后面基本上不去用它了,但是又有可能会用它,而频繁的创建这个对象又比较耗资源,可以声明为弱引用。其实我觉得,大部分时间用弱引用就行了,软引用更多是为了在内存溢出之前多回收点内存。当然了,具体该使用软引用还是弱引用,需要根据实际需要决定。

    说那么多引用,比较抽象,我们看看如何声明使用这些引用,在心里将它们与我们平时所用的强引用的用法上默默地做个比较:

    import java.lang.ref.SoftReference;
    import java.lang.ref.WeakReference;
    
    public class Test {
    
        SoftReference<RefObject> softRefObject;
        WeakReference<RefObject> weakRefObject;
        RefObject softObject;
        RefObject weakObject;
    
        public Test() {
            softObject = new RefObject();
            weakObject = new RefObject();
    
            softRefObject = new SoftReference<Test.RefObject>(softObject);
            weakRefObject = new WeakReference<Test.RefObject>(weakObject);
    
        }
    
    
    
        public static void main(String args[])   {
    
            Test test = new Test();
    
            RefObject softRefObject = test.softRefObject.get();
            RefObject weakRefObject = test.weakRefObject.get();
    
    
            if(softRefObject!=null){
                softRefObject.hello();
            }
    
            if(weakRefObject!=null){
                weakRefObject.hello();
            }
    
        }
    
        class RefObject {
            public void hello(){
                System.out.println("hello reference");
            }
        }
    }
    

    内存分配策略

    内存的分配,主要是在堆里面进行分配(也有可能经过JIT编译后被拆散为标量类型并间接地在栈上分配),堆里面主要是发生在新生代的Eden区中,少数情况下是在老年代中,分配的规则不是固定的,这需要根据当前使用的是哪种垃圾收集器组合还有虚拟机与内存相关的参数设置。

    对象优先在Eden分配

    大多数情况下,对象在新生代Eden区进行分配。当Eden区没有足够空间进行分配时JVM发生一次Minor GC。什么叫Minor GC呢?Minor GC是指发生在新生代的垃圾收集动作,因为Java对象大多具备朝生夕死的特性,所以Minor GC非常频繁,当然了,其回收速度肯定也是比较快的~,与之对应,还有个Full GC或者称为Major GC,是指老年代中的GC,经常会伴随一次Minor GC,Major GC速度一般会比Minor GC速度慢10倍以上!

    大对象直接进入老年代

    所谓的大对象,是指占用大量连续内存空间的Java对象。最经典的大对象就是那种很长的字符串和数组。大对象对于虚拟机来说是个坏消息~我们写程序时,尽量要避免出现一群朝生夕死的大对象。经常出现大对象容易导致内存还有不少空间时就得提前触发垃圾收集以获取足够的空间来存放大对象。

    长期存活的对象将进入老年代

    JVM采用分代收集思想来管理内存,就要去区分哪些是年轻的对象,哪些是老年的对象。我们知道,刚创建的对象肯定是年轻的对象,那么怎么将对象判断为老年呢?

    在Eden区出生,并经过一次Minor GC后仍然存活,并且能被To Suvivor容纳,移动到To Suvivor区后,年龄设置为1。以后每经历一次Minor GC就将年龄加1,当它的年龄达到一个阀值(默认15,也可以更改-XX:MaxTenurinigThreshold来设置),就会被晋级到老年代中。

    动态对象年龄判定

    为了更好地适应不同程序内存情况,JVM并不一定是等到对象年龄达到阀值才将对象晋级到老年代。如果在Survivor空间中的相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年的对象就可以直接进入老年代,无需等到指定的阀值。这句话可能有点绕,不太好理解,我来再解释一下,就是说,假设Survivor的空间大小为max,年龄为y的对象总共有n个,如果y*n>max/2,那么所有年龄大于y的对象全部进入到老年代。

    展开全文
  • Android ViewModel 引入协程

    千次阅读 2019-05-11 16:07:58
    AndroidX Lifecycle v2.1.0 在 ViewModel 中引入 viewModelScope,当 ViewModel 被销毁时它会自动取消协程任务,这个特性真的好用。本文介绍 viewModelScope 使用和内部实现方式,分析 ViewModel 是如何自动取消协程...

    AndroidX Lifecycle v2.1.0 在 ViewModel 中引入 viewModelScope,当 ViewModel 被销毁时它会自动取消协程任务,这个特性真的好用。本文介绍 viewModelScope 使用和内部实现方式,分析 ViewModel 是如何自动取消协程的。

    ViewModel 引入协程

    当我们在 ViewModel 里面需要引入协程,首先要在 ViewModel 中新建一个 CoroutineScope, 用来管理所有协程任务,同时需要 onCleared() 方法里面取消协程任务,模板代码实现如下:

    class MyViewModel : ViewModel() {
    
        private val viewModelJob = SupervisorJob()
        
        private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
        
        override fun onCleared() {
            super.onCleared()
            viewModelJob.cancel() // Cancel all coroutines
        }
        
        fun launchDataLoad() {
            uiScope.launch {
                sortList()
                // Modify UI
            }
        }
        
        suspend fun sortList() = withContext(Dispatchers.Default) {
            // Heavy work
        }
    }
    复制代码

    然而,很多情况我们会经常忘记取消协程,导致出现内存泄漏等各种问题。 遇到这种场景,可以使用 ViewModel 扩展属性 viewModelScope 来优化代码。

    viewModelScope 方式

    注意 lifecycle-viewmodel-ktx 版本号: 2.1.0-beta01

    viewModelScope 管理协程的方式与我们在 ViewModel 引入协程的方式一样,使用非常简单,模板代码实现如下:

    class MyViewModel : ViewModel() {
      
        fun launchDataLoad() {
            viewModelScope.launch {
                sortList()
                // Modify UI
            }
        }
      
        suspend fun sortList() = withContext(Dispatchers.Default) {
            // Heavy work
        }
    }
    复制代码

    viewModelScope 内部实现

    // androidx.lifecycle.ViewModel.viewModelScope
    
    private const val JOB_KEY = "androidx.lifecycle.ViewModelCoroutineScope.JOB_KEY"
    
    val ViewModel.viewModelScope: CoroutineScope
            get() {
                val scope: CoroutineScope? = this.getTag(JOB_KEY)
                if (scope != null) {
                    return scope
                }
                return setTagIfAbsent(JOB_KEY,
                    CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main))
            }
    
    internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
        override val coroutineContext: CoroutineContext = context
    
        override fun close() {
            coroutineContext.cancel()
        }
    }
    复制代码

    分析 viewModelScope 源码有 3 点需要关注:

    1. 注意使用 SupervisorJob 而不是用 Job
    2. 为了 ViewModel 能够取消协程,需要实现 Closeable 接口
    3. viewModelScope 默认使用 Dispatchers.Main, 方便 Activity 和 Fragment 更新 UI

    ViewModel 内部取消协程

    ViewModel 类通过 HashMap 存储 CoroutineScope 对象,当使用 getTag(JOB_KEY) 方法获取对象不存在时,创建一个新的 CoroutineScope 并调用 setTagIfAbsent(JOB_KEY, scope) 方法存储新建的 CoroutineScope 对象。ViewModel 被销毁时内部会执行 clear() 方法,在 clear() 方法中遍历调用 closeWithRuntimeException 取消了 viewModelScope 的协程,实现流程非常清晰。相关代码如下:

    // androidx.lifecycle.ViewModel
    
    // clear() -> closeWithRuntimeException() -> coroutineContext.cancel()
    
    private final Map<String, Object> mBagOfTags = new HashMap<>();
    
    <T> T getTag(String key) {
        synchronized (mBagOfTags) {
            return (T) mBagOfTags.get(key);
        }
    }
    
    <T> T setTagIfAbsent(String key, T newValue) {
        T previous;
        synchronized (mBagOfTags) {
            previous = (T) mBagOfTags.get(key);
            if (previous == null) {
                mBagOfTags.put(key, newValue);
            }
        }
        T result = previous == null ? newValue : previous;
        if (mCleared) {
            closeWithRuntimeException(result);
        }
        return result;
    }
    
    @MainThread
    final void clear() {
        mCleared = true;
        if (mBagOfTags != null) {
            for (Object value : mBagOfTags.values()) {
                closeWithRuntimeException(value);
            }
        }
        onCleared();
    }
    
    private static void closeWithRuntimeException(Object obj) {
        if (obj instanceof Closeable) {
            try {
                ((Closeable) obj).close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
    复制代码

    结论

    如果你也正在使用 MVVM 和协程,非常推荐在 ViewModel 中使用 viewModelScope 方式。不仅简化 ViewModel 代码,而且还能管理协程生命周期。

    转载于:https://juejin.im/post/5cd6f33a6fb9a0323416663e

    展开全文
  • OOAD:面向对象的分析和设计

    千次阅读 2016-09-04 01:38:47
    OOAD:面向对象的分析和设计 1.什么是面向对象 面向对象(Object-Orientation,简称OO)是一种系统建模技术/编程思想。 面向对象编程(Object-Orientation Propramming,简称OOP)是按照OO的方法学来开发程序的编程方式。...
  • PHP对象基础

    千次阅读 2021-03-15 20:02:07
    面向对象:OOP(objected oriented programming)编程 面向过程是一种编程思想 面向对象编程的本质是增加数据和功能的操作主体,即对象 面向对象中的所有的数据和功能多是由主体(对象)来调用和操作 面向对象基础 ...
  • 堆中几乎存放着Java世界中所有的对象实例,垃圾收集器在对堆回收之前,第一件事情就是要确定这些对象哪些还“存活”着,哪些对象已经“死去”(即不可能再被任何途径使用的对象) 1.引用计数算法  很多教科书判断...
  • JavaScript 面向对象编程

    千次阅读 2016-04-26 23:13:09
    为了说明 JavaScript 是一门彻底的面向对象的语言, 首先有必要从面向对象的概念着手 , 探讨一下面向对象中的几个概念:1. 一切事物皆对象 2. 对象具有封装和继承特性 3. 对象对象之间使用消息通信,各自存在信息...
  • JS原生对象与内置对象

    千次阅读 2018-03-05 04:27:38
    一、JS的对象和类型 JS中的所有事物都是对象,包括但不限于字符串、数值、数组、函数等等,还包括自定义对象。 在红宝书中,将JS分为五种基本类型:null、undefined、number、string、boolean和一种复杂类型:...
  • 本文通过对象的创建步骤中的检查加载->分配内存->内存空间初始化->设置->对象初始化,对象的内存布局,什么是垃圾的两种算法以及四种引用,讲述JVM中对象及引用。
  • Object c强引用与引用

    千次阅读 2014-09-06 20:25:13
    strong weak ...ARC引入了新的对象的新生命周期限定,即零引用。如果零引用指向的对象被deallocated的话,零引用的对象会被自动设置为nil。 @property(strong) MyClass *myObjec
  • 【C++】weak_ptr引用智能指针详解

    千次阅读 多人点赞 2020-05-03 15:34:11
    weak_ptr这个指针天生一副小弟的模样,也是在C++11的时候引入的标准库,它的出现完全是为了弥补它老大shared_ptr天生有缺陷的问题。 相比于上一代的智能指针auto_ptr来说,新进老大shared_ptr可以说近乎完美,但是...
  • 前端JS的引入和申明

    千次阅读 2018-11-05 23:20:06
    JS的概念和介绍 问题: ... 网页不能和用户之间进行简单的交互,造成用户的... 是一门浏览器端的类型的基于对象的脚本语言,功能非常庞大。原名:ECMAScript 作用: 实现了网页和用户之间的互动 实现: ...
  • php面向对象详解

    千次阅读 2015-12-08 09:59:30
    1.什么是面向对象?  面向对象编程(Object Oriented Programming, OOP, 面向对象程序设计)是一种计算机编程架构,  OOP的一条基本原则是计算机程序是由单个能够起到子程序作用的单元或对象组合而成  OOP...
  • php8引入jit后

    千次阅读 2019-04-16 16:25:37
    很多人可能对JIT有很深的误解,觉得引入JIT之后性能就能提高10倍跟V8平起平坐了,事实上不是这样的。JIT技术的水很深,动态语言的JIT尤其困难,V8的诞生几乎可以说是一个技术奇迹。以PHP社区的技术水平,我谨慎地不...
  • 全方位深入理解JavaScript面向对象

    万次阅读 多人点赞 2018-03-30 10:13:16
    JavaScript面向对象程序设计 本文会碰到的知识点: 原型、原型链、函数对象、普通对象、继承 读完本文,可以学到 面向对象的基本概念 JavaScript对象属性 理解JavaScript中的函数对象与普通对象 理解prototype...
  • Java对象的4中引用类型

    千次阅读 2016-05-23 12:32:37
    Java对象的4中引用类型  对于需要长期运行的应用程序来说,如果无用的对象所占用的内存空间不能得到即时的释放的话,那么在一个局部的时间段内便形成了事实... JDK里面引入了4种对象引用类型,可以算是强行的
  • javascript中的内置对象和数据结构

    千次阅读 2021-02-24 21:25:34
    基本上所有的程序员都使用过javascript,我们在web中使用javascript,我们在服务器端使用nodejs,js给大家的第一映像就是简单,但是可能并不是所有人都系统的了解过js中的内置对象和数据结构。 今天,一起来看看吧...
  • ARC/OC对象自动管理内存

    千次阅读 2013-12-09 11:13:10
    ARC是一个编译器特征,它提供了对OC对象自动管理内存。ARC让开发者专注于感兴趣的代码和对象的关系,而不用考虑对象的retain和release。 转自hherima的博客 原文:Transitioning to ARC Release Notes(苹果...
  • c# 中的泛型以及强类型与类型

    千次阅读 2015-05-04 15:40:17
    (1)没有泛型的时候,所有的对象都是以object为基础(object是所有对象的基类),如果要使用时必须进行强制类型转换。对于值类型的转换,则会导致不断拆箱、装箱的过程,会造成系统不停地分配内存、垃圾回收、资源...
  • Java引用对象

    千次阅读 2018-12-11 10:09:03
    同样要记住的是软引用、引用、虚引用只有在没有其他强引用指向被引用者时才有意义,它们让你可以在对象通常会成为垃圾回收器的食物时候获得该对象。这可能看起来很奇怪,如果不再持有强引用了,为什么我还关心这个...
  • C++:智能指针(weak_ptr)

    千次阅读 2019-03-29 18:11:03
    智能指针: 一般结合强智能指针使用,它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是强引用的 shared_ptr. weak_ptr只是提供了对管理对象的一个访问手段;weak_ptr 设计的目的是为配合 shared_ptr ...
  • 创建对象的方式用new语句创建对象。使用反射,调用java.lang.Class或java.lang.reflect.Constructor的newInstance()实例方法。调用对象的clone()方法使用反序列化手段,调用java.io.ObjectInputStream对象的...
  • 监督目标检测:Weakly supervised object detection (WSOD) 文章:Min-Entropy Latent Model for Weakly ...因此监督和学习目标之间的不一致性引入对象位置的随机性和对检测器的模糊性。 文章提出熵...
  • 计算机存储的发展(块存储,文件存储,对象存储)

    万次阅读 多人点赞 2018-09-15 15:04:08
    对象存储 1、对象 2、对象存储设备 3、元数据服务器(Metadata Server,MDS) 4、对象存储系统的客户端Client 三者之间异同比较 参考文献 如果要实现一个计算机,那么这个计算机一定要有以下的三个部分构成:...
  • 为什么要有JavaScript,(它有什么作用)? ... 而CSS:负责呈现内容的方式 ... 现在接触的JS:负责内容的行为 ...实际上,我们的页面更多的是要求动态的,比如说新浪网的很多新闻和图片都是每天更新的,我们不...javascript的引入
  • 依赖注入与对象间关系

    千次阅读 2018-09-11 23:12:30
    目前,在.NET和Java领域已经有相当多基于DI思想的对象容器,如:Spring,Unity等。本文试图避免重复性地介绍DI基础知识和DI容器的使用,而是希望深一层探讨DI的本质和对象间关系,以达到合理设计避免滥用DI的目的。 ...
  • PB面向对象编程研究(一)

    千次阅读 2014-03-10 14:10:33
    PB面向对象编程研究 1. 前言 众所周知,PowerBuilder是第四代面向对象开发语言。 面向对象程序设计的根本目的是实现数据的...进而实现结构单元与结构单元之间的耦合性。这里所说的结构单元就是类。 面向对象的继承性
  • 事件模式

    2013-06-16 13:43:07
    在应用程序中,为了与将处理程序附加到事件源的侦听器对象配合,附加到该事件源的处理程序可能不会被破坏。这种情况可能会导致内存泄漏。 Windows Presentation Foundation (WPF) 引入了可用于解决此问题的设计模式...
  • swift对象存储

    万次阅读 2016-05-23 12:18:50
    swift对象存储简介OpenStack Object Storage(Swift)是OpenStack开源云计算项目的子项目之一,被称为对象存储,提供了强大的扩展性、冗余和持久性。对象存储,用于永久类型的静态数据的长期存储。 Swift 最初是由 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 61,020
精华内容 24,408
关键字:

弱引入对象