精华内容
下载资源
问答
  • 我的java程序是bat调用jar包启动的,运行一段时间会出现JAVA SE。。。 已停止运行的弹框,这个时候java进程也没关闭,是处于一种卡死阻塞的状态,浏览器访问系统会一直连接响应,但是...
  • 大家仔细观察,这次程序可以一直运行,并且能够持续接受我们的输入,并且根据我们输入的预定指令进行运行。这是怎么做到的呢? 其实要想我们的程序能够按照项目要求来运行,最终可以把完成的程序让用户来使用。 ...

    前言:如果你还是个菜鸟,如果你还在为学习编程而找不到门路,则这套《面向对象编程从小白到王者系列》是你最好的选择,本套教程为系列教程由简入深,循序渐进。所有的代码都会在文中完整的展示出来。无论你是学习JAVA还是C#本教程都是你入门学习的最佳选择。

    老规矩,我们先来看一下最后的完成效果,如图:

    大家仔细观察,这次程序可以一直运行,并且能够持续接受我们的输入,并且根据我们输入的预定指令进行运行。这是怎么做到的呢?

    其实要想我们的程序能够按照项目要求来运行,最终可以把完成的程序让用户来使用。

    实现上边展示的效果,需要用到我们在上一篇文章中所讲的流程控制语句do......while循环或while循环,至于while循环语句使用语法在上一篇文中已经介绍,这里就不多讲了。

    下面是项目代码:

    /* using是应用命名空间的关键字,
     * using后面跟的就是被引用进来的命名空间*/
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    /* namespace是命名空间关键字,
     * MailList是我们创建时起的项目名称*/
    namespace MailList
    {
        class Program //Program指的是类的名字
        {
            /*Main指的是方法(函数)的名字,
             * 这Main是程序运行主函数,
             * 不能修改其名称,
             * 而且static关键字也必须有
             * c#编程所用到的关键字和意思可以浏览《学习C#必须掌握的那些关键字,新手必看》*/
            static void Main(string[] args) 
            {
                bool Running = true;//用于记录当前系统运行状态,默认启动后自动保持运行。
                Console.WriteLine("欢迎跟着浩洋学编程");//将欢迎信息显示在界面上
                Console.WriteLine("请输入你的名字");//提示用户要干什么
                string name = Console.ReadLine();//接收用户输入信息
                Console.WriteLine("很高兴认识你,"+name);//将用户输入的信息显示在界面上
                Console.WriteLine("=====一下是通讯录列表=====");
                Console.WriteLine("1、曹操");
                Console.WriteLine("2、诸葛亮");
                Console.WriteLine("3、刘备");
                Console.WriteLine("4、关于");
                Console.WriteLine("5、张飞");
                Console.WriteLine("6、赵云");
                Console.WriteLine("7、吕布");
                Console.WriteLine("8、周瑜");
                Console.WriteLine("9、魏延");
                Console.WriteLine("10、黄盖");
                Console.WriteLine("=====通讯录列表显示完毕=====");
                Console.WriteLine("输入姓名即可查询相关人员的详细信息");
                Console.WriteLine("输入exit退出程序");          
    
                do
                {
                    name = Console.ReadLine();//接收用户输入信息
                    switch (name)
                    {
                        case "曹操":
                            Console.WriteLine(" 曹操");
                            Console.WriteLine(" 性别:男");
                            Console.WriteLine(" 年龄:1000岁");
                            Console.WriteLine(" 联系电话:12345678901");
                            break;
                        case "刘备":
                            Console.WriteLine(" 刘备");
                            Console.WriteLine(" 性别:男");
                            Console.WriteLine(" 年龄:1000岁");
                            Console.WriteLine(" 联系电话:12354678901");
                            break;                    
                        case "诸葛亮":
                            Console.WriteLine(" 诸葛亮");
                            Console.WriteLine(" 性别:男");
                            Console.WriteLine(" 年龄:1000岁");
                            Console.WriteLine(" 联系电话:12354678901");
                            break;
                        case "关于":
                            Console.WriteLine(" 关于");
                            Console.WriteLine(" 性别:男");
                            Console.WriteLine(" 年龄:1000岁");
                            Console.WriteLine(" 联系电话:12354678901");
                            break;
                        case "张飞":
                            Console.WriteLine(" 张飞");
                            Console.WriteLine(" 性别:男");
                            Console.WriteLine(" 年龄:1000岁");
                            Console.WriteLine(" 联系电话:12354678901");
                            break;
                        case "赵云":
                            Console.WriteLine(" 赵云");
                            Console.WriteLine(" 性别:男");
                            Console.WriteLine(" 年龄:1000岁");
                            Console.WriteLine(" 联系电话:12354678901");
                            break;
                        case "吕布":
                            Console.WriteLine(" 吕布");
                            Console.WriteLine(" 性别:男");
                            Console.WriteLine(" 年龄:1000岁");
                            Console.WriteLine(" 联系电话:12354678901");
                            break;
                        case "周瑜":
                            Console.WriteLine(" 周瑜");
                            Console.WriteLine(" 性别:男");
                            Console.WriteLine(" 年龄:1000岁");
                            Console.WriteLine(" 联系电话:12354678901");
                            break;
                        case "魏延":
                            Console.WriteLine(" 魏延");
                            Console.WriteLine(" 性别:男");
                            Console.WriteLine(" 年龄:1000岁");
                            Console.WriteLine(" 联系电话:12354678901");
                            break;
                        case "黄盖":
                            Console.WriteLine(" 黄盖");
                            Console.WriteLine(" 性别:男");
                            Console.WriteLine(" 年龄:1000岁");
                            Console.WriteLine(" 联系电话:12354678901");
                            break;
                        case "exit"://如果用户输入了exit则执行下面语句
                            Running = false;//如果用户在上面输入了exit,则将变量Running的值改变成false     
                            break;
                        default:
                            Console.WriteLine("没有查询到您要找的信息");
                            break;
                    }
                } while (Running);//判断是否继续运行,如果Running的值是false,则不再运行     
            }
        }
    }

    上面代码中,我只是将接收用户输入的name = Console.ReadLine();这句代码放在了do......while循环里。

    在运行开始的时候声明了变量Running用来标记是否退出程序的变量,并初始化变量为true就是不退出的意思,这样已启动程序,循环就会一直运行,每次循环都会自动检查

    Running是不是变成false,当发现Running变成false后则循环就会不再执行了,程序会继续执行while下面的代码,但是while下面已经没有代码,所以就会自动提示"按任意键退出".

    本次核心学习内容就是:接收输入语句Console.ReadLine()配合while循环加上bool类型变量Running完成了程序持续运行的效果。

    可以点击下载项目代码-C#面向对象入门实战-通讯录01

    C#面向对象入门实战-通讯录01

    学习路线:面向对象编程从小白到王者系列-01创建我的第一个程序

                      面向对象编程从小白到王者系列-02认识项目结构

                      面向对象编程从小白到王者系列-03如何在显示和输入

                      面向对象编程从小白到王者系列-04编程里神奇的变量

                      面向对象编程从小白到王者系列-05查询和显示通讯录

    工具知识:学习C#必须掌握的那些关键字,新手必看

    基础知识:面向对象编程从小白到王者系列-认识对象

     

    展开全文
  • 以前都没有发现什么问题,但是最近在某个客户Linux机器(RedHat4企业版)上运行java后出现了大量的java进程现象,而且很快就将内存耗尽了。 查了相关资料,说是Java多线程在Linux环境下会采用多进程处理,...
  • 内存泄露总结内存管理的目的就是...Java内存分配策略Java程序运行时的内存分配策略共有三种,分别是静态分配、栈式分配、堆式分配,对应的,三种存储策略使用的内存空间主要分别是静态存储区(方法区)、栈区和堆区。

    内存泄露总结

    内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题。内存泄漏的问题简单粗俗的讲,就是该被释放的对象没有释放,一直被某个或某些实例所持有却不再被使用导致GC不能回收。


    Java内存分配策略

    Java程序运行时的内存分配策略共有三种,分别是静态分配、栈式分配、堆式分配,对应的,三种存储策略使用的内存空间主要分别是静态存储区(方法区)、栈区和堆区。

    静态存储区(方法区):主要存放静态数据、全局static数据和常量。这块内存在程序编译时就已经分配好,并且在程序整个运行期间都存在。
    
    栈区:当方法被执行时,方法体内的局部变量(其中包括基础数据类型、对象的引用)都在栈上创建,并在方法执行结束时这些局部变量所持有的内存将会自动被释放。因为栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
    
    堆区:又称动态内存分配,通常就是指在程序运行时直接new出来的内存,也就是对象的实例。这部分内存在不使用时将会有Java垃圾回收器来负责回收。
    

    栈和堆的区别

    在方法体内定义的(局部变量)一些基本类型的变量和对象的引用变量都是在方法的栈内存中分配的。当在一段方法块中定义一个变量时,Java就会在栈中为该变量分配内存空间,当超过该变量的作用域后,该变量也就无效了,分配给它的内存空间也将被释放掉,该内存空间可以被重新使用。

    堆内存用来存放所有由new创建的对象(包括该对象中的所有成员变量)和数组。在堆中分配的内存,将由垃圾回收器来自动管理。在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,这个特殊的变量就是我们上面说的引用变量。我们可以通过这个引用变量来访问堆中的对象或者数组。

    举个例子:
        public class Sample{
            int s1 = 0;
            Sample mSample1 = new Sample();
    
            public void method(){
                int s2 = 1;
                Sample mSample2 = new Sample();独立8斤
            }
    
        }
        Sample mSample3 = new Sample();
    
    Sample类的局部变量s2和引用变量mSample2都存在于栈中,但是mSample2指向的对象是存在于堆中的。mSample3指向的对象实体存放在堆上,包括这个对象的所有成员变量s1和mSample1,而它自己存在于栈中。
    
    结论:
    局部变量的基本数据类型和引用存储在栈中,引用的对象实体存储在堆中。--因为他们属于方法中的变量,生命周期随着方法而结束。
    成员变量全部储存在堆中(包括基本数据类型,引用和引用对象的实体)--因为他们属于类,类对象终究是要被new出来使用的。
    

    Java是如何管理内存?

    Java的内存管理就是对象的分配和释放问题。在Java中,程序员通过关键字new为每个对象申请内存空间(基本对象除外),所有的对象都在堆(Heap)中分配空间。另外,对象的释放是由GC决定和执行的。在Java中,内存的分配是由程序完成的,而内存的释放,是由GC完成的,这种收支两条线的方法确实简化了程序员的工作。但同时,他也加重了JVM的工作。这也是Java程序运行速度较慢的原因之一。因为,GC为了能够正确释放对象,GC必须监控每个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC都需要进行监控。

    监视对象状态是为了更加准确地、及时地释放对象,而是释放对象的根本原则就是该对象不再被引用。

    什么是Java中的内存泄漏

    在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与之相连;其次,这些对象是无用的,即程序以后不会再使用这些对象,如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏。这些对象不会被GC所回收,然而它却占用内存。

    在C++中,内存泄漏的范围更大一些,有些对象被分配了内存空间,然后却不可达,由于C++中没有GC,这些内存将永远不会回收回来。在Java中,这些不可达的对象都由GC来负责回收,因此程序员不需要考虑这部分的内存泄漏。

    对于程序员来说,GC基本是透明的不可见的。虽然,我们只有几个函数可以访问GC,例如运行GC的函数System.gc(),但是根据Java语言规范定义,该函数不保证JVM的垃圾回收器一定执行。因为,不同的JVM实现者可能使用不同的算法管理GC。通常,GC的线程优先级别较低。JVM调用GC的策略也是有很多种,有的是内存使用达到一定程度时,GC才开始工作,有的是定时执行的,有的是平缓执行GC,有的是中断式执行GC,但是通常来说,我们不需要关心这些,除非在一些特定的场合,GC的执行影响应用程序的性能,例如基于Web的实时系统,如网络游戏等,用户不希望GC突然中断应用程序执行而进行垃圾回收,那么我们需要调整GC的参数,让GC能够通过平缓的方式释放内存,例如将垃圾回收分解为一系列的小步骤执行,Sun提供的HotSpot JVM就支持这一特性。

    同样给出一个Java内存泄漏的典型例子。

    Vector v = new Vector(10);   
        for(int i = 1;i

    详解Java中的内存泄漏

    1. Java内存回收机制

    不论哪种语言的内存分配方式,都需要返回所分配内存的真实地址,也就是返回一个指针到内存块的首地址。Java中对象是采用new或者反射的方法创建的,这些对象的创建都是在堆中分配的,所有对象的回收都是由Java虚拟机通过垃圾回收机制完成的。GC为了能够正确释放对象,会监控每个对象的运行状况,对他们的申请、引用、被引用、赋值等状况进行监控,Java会使用有向图的方法进行管理内存,实时监控对象是否可以达到,如果不可到达,则就将其回收,这样也可以消除引用循环的问题。在Java语言中,判断一个内存空间是否符合垃圾收集标准有两个:一个是给对象赋予了空值null,以下在没有调用过,另一个是给对象赋予了新值,这样就重新分配了内存空间。

    2. Java内存泄漏引起的原因

    内存泄漏是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时的释放,从而造成内存空间的浪费称为内存泄漏。内存泄漏有时不严重不易察觉,这样开发者就不知道存在内存泄漏,但有时候也会很严重,会提示你Out of Memory。


    Java内存泄漏的根本原因是什么呢?长生命周期的对象持有段生命周期对象的引用就很可能发生内存泄漏,尽管短生命周期对象已经不再需要,但是因为长生命周期持有它的引用而导致不能被回收,这就是Java中内存泄漏的发生场景。具体主要有如下几大类:
    (1)静态集合类引起的内存泄漏

    像HashMap、Vector等的使用最容易出现内存泄漏,这些静态变量的生命周期和应用程序一致,他们所引用的所有的对象Object也不能被释放,因为他们将一致被Vector等引用着。

    static Vector v = new Vector(10);
    for(int i=0;i
    (2)当集合里面的对象属性被修改后,再调用remove()方法时不起作用。

    例如:

    public static void main(String[] args){
        Set set = new HashSet();
        Person p1 = new Person("唐僧","pwd1",25);
        Person p2 = new Person("孙悟空","pwd2",26);
        Person p3 = new Person("猪八戒","pwd3",27);
        set.add(p1);
        set.add(p2);
        set.add(p3);
        System.out.println("总共有:"+set.size()+" 个元素!"); //结果:总共有:3 个元素!
        p3.setAge(2); //修改p3的年龄,此时p3元素对应的hashcode值发生改变
    
        set.remove(p3); //此时remove不掉,造成内存泄漏
    
        set.add(p3); //重新添加,居然添加成功
        System.out.println("总共有:"+set.size()+" 个元素!"); //结果:总共有:4 个元素!
        for (Person person : set){
            System.out.println(person);
        }
    }
    
    (3)监听器

    在java编程中,我们都需要和监听器打交道,通常一个应用当中会用到很多监听器,我们会调用一个控件的诸如addXXXListener()等方法来增加监听器,但往往释放对象的时候却没有记住去删除这些监听器,从而增加了内存泄漏的机会

    (4)各种连接

    比如数据库连接(dataSourse.getConnection()),网络连接(socket)和io连接,除非其显示的调用了其close()方法将其连接关闭,否则是不会自动被GC回收的。由于ResultSet和Statement对象可以不进行显示回收,但Connection一定要显示回收,因为Connection对象在任何时候都无法自动回收,而Connection一旦回收,ResultSet和Statement对象就会立即为Null。但是如果使用连接池,情况就不一样了,除了要显示地关闭连接,还必须显示地关闭ResultSet和Statement对象(关闭其中一个,另一个也会自动关闭),否则就会造成大量的Statement无法释放,从而引起内存泄漏。这种情况下一般都会在try里面去连接,在finally里面释放连接。

    (5)内部类和外部模块的引用

    内部类的引用是比较容易遗忘的一种,而且一旦没释放可能导致一系列的后继类对象没有释放。此外程序员还要小心外部模块不经意的引用,例如程序员A负责A模块,调用了B模块的一个方法如:public void registerMsg(Object o);这种调用就要非常的小心,传入了一个对象,很可能模块B就保持了该对象的引用,这时候就需要注意模块B是否提供相应的操作去除引用。

    (6)单例模式

    不正确使用单例模式是引起内存泄漏的一个常见问题,单例对象在初始化后将在JVM的整个生命周期中存在(以静态变量的方式),如果单例对象持有外部的引用,那么这个对象将不能被JVM正常回收,导致内存泄漏,考虑下面的例子:

    class A{
        public A(){
            B.getInstance().setA(this);
        }
        ....
    }
    //B类采用单例模式
    class B{
        private A a;
        private static B instance=new B();
        public B(){}
        public static B getInstance(){
            return instance;
        }
        public void setA(A a){
            this.a=a;
        }
        //getter...
    } 
    

    显然B采用singleton模式,它持有一个A对象的引用,而这个A类的对象将不会被回收,想象一下如果A是个比较复杂的对象或者集合类型会发生什么情况。


    Android中常见的内存泄漏汇总

    1.集合类泄露

    集合类如果仅仅有添加元素的方法,而没有相应的删除机制,导致内存被占用。如果这个集合类是全局性的变量(比如类中的静态属性,全局性的map等即有静态引用或final一直指向它),那么没有相应的删除机制,很可能导致集合所占用的内存只增不减。比如上面的典型例子就是其中一种情况,当然实际上我们在项目中肯定不会写这样的代码,但是稍不注意还是很容易出现这种情况,比如我们都喜欢通过HashMap做一些缓存之类的事情,这种情况一定要注意。

    2.单例造成的内存泄漏

    由于单例的静态特性使得其生命周期跟应用的生命周期一样长,如果使用不恰当,很容易造成内存泄漏,比如下面一个例子:

    public class AppManager{
        private static AppManager instanse;
        private Context context;
        private AppManager(Context context){
            this.context = context;
        }        
        public static AppManager getInstance(Context context){
            if(instance == null){
                instance = new AppManager(context);
            }
            return instance;
        }
    }
    

    这是一个普通的单例模式,当创建这个单例的时候,由于需要传入一个Context,所以这个Context的生命周期的长短至关重要:

    1. 如果此时传入的是Application的Context,因为Application的生命周期就是整个应用的生命周期,所以这将没有任何问题。

    2. 如果此时传入的是Activity的Context,当这个Context所对应的Activity退出时,由于该Context的引用被单例对象所持有,整个生命周期等于整个应用的生命周期,所以当前Activity退出时它的内存不会被回收,这就造成了泄漏了。

    正确的方式应该改为下面这种方式:

    public class AppManager{
        private static AppManager instanse;
        private Context context;
        private AppManager(Context context){
            this.context = context.getApplicationContext();//使用Application的context
        }        
        public static AppManager getInstance(Context context){
            if(instance == null){
                instance = new AppManager(context);
            }
            return instance;
        }
    }
    

    或者这样写,连Context都不用传进来了:

    在你的Application里面添加一个静态方法,getContext()返回Application的context
    ...
    context = getApplicationContext();
    ...
    /**获取全局的Context*/
    public static Context getContext(){
        return context;
    }
    public class AppManager{
        private static AppManager instanse;
        private Context context;
        private AppManager(){
            this.context = AppManager.getContext();
        }        
        public static AppManager getInstance(Context context){
            if(instance == null){
                instance = new AppManager(context);
            }
            return instance;
        }
    }
    

    3.匿名内部类/非静态内部类和异步线程

    非静态内部类创建静态实例造成的内存泄漏
    有的时候我们可能会在启动频繁的Activity中,为了避免重复创建相同的数据资源,可能会出现这种写法:

    public class MainActivity extends AppCompatActivity{
        private static TestResource mResource = null; 
        @Override
        protected void onCreate(Bundle saveInstanceState){
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            if(mResource==null){
                mResource = new TestResource();
            }
            //...
        }
    
        class TestResource{
            //...
        }
    }
    

    这样就在Activity内部创建了一个非静态内部类的单例,每次启动Activity时都会使用该单例的数据,这样虽然避免了资源的重复创建,不过这种写法却会造成内存泄漏,因为非静态内部类默认会持有外部类的引用,而该非静态内部类又创建了一个静态的实例,该实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该Activity的引用,导致Activity的内存资源不能正常回收。
    正确的做法为:

    将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用Context,请按照上面推荐的使用Application的Context。当然,Application的context也不是万能的,所以也不能随便乱用,对于有些地方则必须使用Activity的Context(如start an Activity、show a dialog)。
    其实Application和Service可以启动一个Activity,不过需要创建一个新的task任务队列,而对于Dialog而言,只有在Activity中才能创建。

    4.匿名内部类

    android开发经常会继承实现Activity/Fragment/View,此时如果你使用了匿名类,并被异步线程持有了,那要小心了,如果没有任何措施这样一定会导致泄漏。

    public class MainActivity extends Activity{
        ...
        Runnable ref1 = new MyRunnable();
        Runnable ref2 = new MyRunnable(){
            @Override
            public void run(){
                ...
            }
        };
        ...
    }
    

    ref1和ref2的区别是,ref2使用了匿名内部类。此时ref2这个匿名类的实现对象里面多了一个引用,也就是说当前的MainActivity实例会被ref2持有,如果将这个引用再传入一个异步线程,此线程和此Activity生命周期不一致的时候,就造成了Activity的泄露。

    5.Handler造成的内存泄漏

    Handler的使用造成的内存泄漏问题应该说是最为常见了,很多时候我们为了避免ANR而不在主线程进行耗时操作,在处理网络任务或者封装一些请求回调等api都借助Handler来处理,但Handler不是万能的,对于Handler的使用代码编写一不规范即有可能造成内存泄漏。另外,我们知道Handler、Message和MessageQueue都是相互关联在一起的,万一Handler发送的Message尚未被处理,则该Message及发送它的Handler对象将被线程MessageQueue一直持有。

     public class SampleActivity extends Activity {
        private final Handler mLeakyHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
              // ...
            }
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // Post a message and delay its execution for 10 minutes.
            mLeakyHandler.postDelayed(new Runnable() {
            @Override
              public void run() { /* ... */ }
            }, 1000 * 60 * 10);
    
            // Go back to the previous Activity.
            finish();
        }
    }
    

    在该SampleActivity中声明了一个延迟10分钟执行的消息Message,mLeakyHandler将其push进了消息队列MessageQueue里,当该Activity被finish掉时,延迟执行任务的Message还会继续存在主线程中,它持有该Activity的Handler引用,所以此时finish掉的Activity就不会被回收了从而造成内存泄漏(因Handler为静态内部类,它会持有外部类的引用,在这里就是指SampleActivity)。
    修复方法:在Activity中避免使用非静态内部类,比如上面我们将Handler声明为静态的,其存活期跟Activity的生命周期就无关了,同时通过弱引用的方式引入Activity,避免直接将Activity作为context传进去。

        public class SampleActivity extends Activity {
    
      /**
       * Instances of static inner classes do not hold an implicit
       * reference to their outer class.
       */
      private static class MyHandler extends Handler {
        private final WeakReference mActivity;
    
        public MyHandler(SampleActivity activity) {
          mActivity = new WeakReference(activity);
        }                          
    
        @Override
        public void handleMessage(Message msg) {
          SampleActivity activity = mActivity.get();
          if (activity != null) {
            // ...
          }
        }
      }
    
      private final MyHandler mHandler = new MyHandler(this);
    
      /**
       * Instances of anonymous classes do not hold an implicit
       * reference to their outer class when they are "static".
       */
      private static final Runnable sRunnable = new Runnable() {
          @Override
          public void run() { /* ... */ }
      };
    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        // Post a message and delay its execution for 10 minutes.
        mHandler.postDelayed(sRunnable, 1000 * 60 * 10);
    
        // Go back to the previous Activity.
        finish();
      }
    }
    

    综述,即推荐使用静态内部类+WeakReference这种方式。每次使用前注意判空。
    前面提到了WeakReference,所以这里就简单的说一下Java对象的几种引用类型。
    Java对引用的分类有StrongReference,SoftReference,WeakReference,PhatomReference四种。
    在Android应用开发中,为了防止内存溢出,在处理一些占用内存大而且周期较长的对象时候,可以尽量应用软引用和弱引用技术。
    软/弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。利用这个队列可以得知被回收的软/弱引用的对象列表,从而为缓冲器清楚已失效的软/弱引用。

    假设我们的应用会用到大量的默认图片,比如应用中有默认的头像,默认游戏图标等等,这些图片很多地方会用到,如果每次都去读取图片,由于读取文件需要硬件操作,速度较慢,会导致性能较低。所以我们考虑将图片缓存起来,需要的时候直接从内存中读取出来,但是,由于图片占用的内存空间比较大,缓存很多图片需要很多的内存,就可能比较容易发生OOM,这是我们可以考虑使用软/弱引用计数来避免这个问题的发生,以下就是高速缓冲器的雏形:


    首先定义一个HashMap保存软引用对象。

    public class CacheBySoftRef {
        //首先定义一个HashMap,保存软引用对象
        private Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>();
    
        //再来定义一个方法,保存Bitmap的软引用到HashMap
        public void addBitmapToCache(String path) {
            //强引用的Bitmap对象
            Bitmap bitmap = BitmapFactory.decodeFile(path);
            //软引用的Bitmap对象
            SoftReference<Bitmap> softBitmap = new SoftReference<Bitmap>(bitmap);
            //添加该对象到Map中使其缓存
            imageCache.put(path, softBitmap);
        }
    
        //获取的时候,可以通过SoftReference的get()方法得到Bitmap对象
        public Bitmap getBitmapByPath(String path) {
            //从缓存中取软引用的Bitmap对象
            SoftReference<Bitmap> softBitmap = imageCache.get(path);
            //判断是否存在软引用
            if (softBitmap == null) {
                return null;
            }
            //通过软引用取出Bitmap对象,如果由于内存不足Bitmap被回收,将取得空,如果未被回收则可重复使用,提高速度。
            Bitmap bitmap = softBitmap.get();
            return bitmap;
    
        }
    }
    

    使用软引用以后,在OutOfMemory异常发生之前,这些缓存的图片资源的内存空间可以被释放掉的,从而避免内存达到上限,避免Crash发生。

    如果只是想避免OutOifMemory异常的发生,则可以使用软引用。如果对于应用的性能更在意,想尽快回收一些占用内存比较大的对象,则可以使用弱引用。

    另外可以根据对象是否经常使用来判断选择软引用还是弱引用。如果该对象可能会经常使用的,就尽量使用软引用,如果该对象不被使用的可能性更大一些,就可以使用弱引用。

    回到主题,前面所说的创建一个静态的Handler内部类,然后对Handler持有的对象使用弱引用,这样在回收时也可以回收Handler持有的对象,但是这样做虽然避免了Activity泄漏,不过Looper线程的消息队列中还是可能会有待处理的消息,所以我们在Activity的Destory时或者Stop时应该移除消息队列MessageQueue中的消息。

    6.尽量避免使用static成员变量

    如果成员变量被声明为static,那我们都知道其生命周期将与整个app进程生命周期一样。
    这会导致一系列问题,如果你的App进程设计上是常驻内存的,那即是app切换到后台,这部分内存也不会释放。按照现在手机app内存管理机制,占内存较大的后台进程将优先回收,因为如果此app做过进程互相保活,那么会造成app在后台频繁重启。

    7.避免override finalize()

    8.资源为关闭造成的内存泄漏

    对于使用了BroadcastReceiver,ContentObserver,File,游标Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时即是关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。

    9.一些不良代码造成的内存压力

    比如,Bitmap没有调用recycle()方法,对于Bitmap对象在不使用时候,我们应该先调用recycle()释放内存,然后将它设置为null,因为家在Bitmap对象的内存空间,一部分是Java的,一部分是C的(因为Bitmap分配的底层是通过JNI调用的)。而这个recycle()就是针对C部分的内存释放。构造Adapter时,没有使用缓存的convertView,每次都在创建新的convertView,这里一定要做listView的优化。

    总结

    对Activity等组件的引用应该控制在Activity的生命周期内;如果不能就考虑使用getApplicationContext或者getApplication,以避免Activity被外部长生命周期的对象引用而泄漏。


    尽量不要在静态变量或者静态内部类中使用非静态内部成员变量(包括context),即使要使用,也要考虑适时把外部成员变量置空;也可以在内部类中使用弱引用来引用外部类的变量。


    对于生命周期比Activity长的内部类对象,并且内部类中使用了外部类的成员变量,可以这样做避免内存泄漏:
    将内部类改为静态内部类、静态内部类中使用弱引用来引用外部类的成员变量。


    Handler所持有的引用对象最好也是用弱引用,资源释放时也可以清空Handler里面的消息。


    在Java的实现过程中,也要考虑其对象释放,最好的方法是在不使用某对象时,显式地将此对象赋值为null,比如使用完Bitmap后先调用recycle(),再赋为null,清空对图片等资源有直接引用或者间接引用的数组(使用array.clear();array=null)等,最好遵循谁创建谁释放的原则。


    正确关闭资源,对于使用了BroadcastReceiver、contentObserve、File、游标Cursor、Stream、Bitmap等资源,应该在Activity销毁时及时关闭或者注销。


    保持对对象生命周期的敏感,特别注意单例、静态对象、全局性集合等的生命周期。

    展开全文
  • 但编写的小程序一直都只是在开发软件的JVM虚拟机上运行,我想知道怎么样才能我的程序离开JVM。 就像《植物大战僵尸》,我在开发软件里有写出一个可以运行的,但如果要给其他人用的话我就不知道怎么做了。 求...
  • JAVA内存问题。

    2011-01-04 15:33:54
    1:以前一直没注意,这几天突然发现windows的任务管理器 里面的内存和程序真正运行的时候是不一样的。当时就迷惑了。JAVA启动后就是一个JAVA进程,我们都知道,里面包含了JAVA虚拟机还有一些其他的东西。所以任务...
  • java tutorial下载

    2009-09-17 14:23:50
    但是java web start图表肯定是没了,重装了jdk还是没有,怎么办,sun网站的那些jnlp程序我就无法直接运行,貌似说我没有装jre等等,只能把jnlp文件下载到本地,然后在命令行通过javaws运行,如何才能系统知道我...
  • java 面试题 总结

    2009-09-16 08:45:34
    异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误。java编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛出未被捕获的...
  • Python实现mapreduce程序

    千次阅读 2016-07-14 14:52:34
    之前面试曾遇到面试官用python代码实现mapreduce中最简单的demo WordCount,由于之前一直java来写hadoop程序,突然转到python,是我产生了质疑,python与hadoop应该是不兼容的,即使写出来程序,到时候怎么运行?...

    一:目的

    之前面试曾遇到面试官让用python代码实现mapreduce中最简单的demo WordCount,由于之前一直用java来写hadoop程序,突然转到python,是我产生了质疑,python与hadoop应该是不兼容的,即使写出来程序,到时候怎么运行?一头雾水最后导致面试失败。后来通过查阅资料,研究mapreduce的底层实现,发现尽管Hadoop框架是用Java编写的,但是为Hadoop编写的程序不必非要Java写,还可以使用其他语言开发,比如Python或C++(Haoop在0.14.1版本提供C++ API),而mapreduce只是一种思想,跟语言无关。$HADOOP_HOME/src/examples/python/WordCount.py,你就可以明白我的意思了。
    现在将会采用python语言实现wordcount并在hadoop上运行实现。

    二:Python代码

    map实现:

    下面Python代码的一个“窍门”是我们将使用Hadoop流API(可以看下相关的维基条目)来帮助我们通过STDIN(标准输入)和STDOUT(标准输出)在Map和Reduce代码间传递数据。我们只是使用Python的sys.stdin读取输入数据和打印输出到sys.stdout。这就是我们需要做的,因为Hadoop流将处理好一切。将下面的代码保存在文件 /home/hduser/mapper.py 中。它将从STDIN读取数据,拆分为单词并输出一组映射单词和它们数量(中间值)的行到STDOUT。尽管这个Map脚本不会计算出单词出现次数的总和(中间值)。相反,它会立即输出( 1)元组的形式——即使某个特定的单词可能会在输入中出现多次。在我们的例子中,我们让后续的Reduce做最终的总和计数。当然,你可以按照你的想法在你自己的脚本中修改这段代码,但是,由于教学原因,我们在本教程中就先这样做。:-)
    请确保该文件具有可执行权限(chmod +x /home/hduser/mapper.py ),否则你会遇到问题。
    这里写图片描述

    reduce实现:

      将下面的代码保存在文件 /home/hduser/reducer.py 中。它将从STDIN读取mapper.py的结果(因此mapper.py的输出格式和reducer.py预期的输入格式必须匹配),然后统计每个单词出现的次数,最后将结果输出到STDOUT中。
      请确保该文件具有可执行权限(chmod +x /home/hduser/reducer.py ),否则你会遇到问题。
      这里写图片描述

    代码测试(cat data | map | sort | reduce):

    在MapReduce作业中使用它们之前,我建议先在本地测试你的mapper.py和reducer.py脚本。否则,你的作业可能成功完成了但没有作业结果数据或得到了不是你想要的结果。如果发生这种情况,很有可能是你(或我)搞砸了。这里有一些想法,关于如何测试这个Map和Reduce脚本的功能。
    这里写图片描述

    运行代码:

    下载示例输入数据

      下载每个文件为纯文本文件,以UTF-8编译并且将这些文件存储在一个临时目录中,如/tmp/gutenberg。

      说明:你将需要在你的Cloudera虚拟机中打开浏览器。选择适当的文件下载(UTF-8 版本),它将显示在你的浏览器中。点击鼠标右键按钮来保存该文件。给它一个合适的名称(如”Ulysses”),并注意它将保存在下载目录中。
      这里写图片描述
     将本地示例数据拷贝到HDFS

      在我们运行实际的MapReduce作业前,我们首先必须从我们本地文件系统中拷贝文件到Hadoop的HDFS内。

      *说明:

      我们假设你是在你的下载目录中。我们必须在HDFS中创建一个子目录,然后拷贝文件过来。最后,我们验证拷贝文件成功。

      首先,我们在HDFS中创建子目录MyFirst:

      [cloudera@quickstart Downloads]$ hadoop fs -mkdir MyFirst

      然后,我们拷贝文件。注意,三个文件以.txt结尾:

      [cloudera@quickstart Downloads]$ hadoop fs -copyFromLocal *.txt MyFirst

      最后,我们验证拷贝成功:

      [cloudera@quickstart Downloads]$ hadoop fs -ls MyFirst

      Found 3 items

      -rw-r–r– 1 cloudera cloudera 1423803 2014-11-30 08:02 MyFirst/Leonardo.txt

      -rw-r–r– 1 cloudera cloudera 674570 2014-11-30 08:02 MyFirst/OutlineOfScience.txt

      -rw-r–r– 1 cloudera cloudera 1573150 2014-11-30 08:02 MyFirst/Ulysses.txt
     这里写图片描述
    运行MapReduce作业

      *说明:

      运行MapReduce作业,敲入如下命令:

      [cloudera@quickstart ~]$ hadoop jar /usr/lib/hadoop-0.20-mapreduce/contrib/streaming/hadoop-streaming.jar -file mapper.py -mapper mapper.py

      -file reducer.py -reducer reducer.py -input MyFirst/* -output MyFirst4-output

      你会收到有关文件被弃用的警告,不用担心。重要的是:当你发出这条命令时,输出目录(在这个示例中是MyFirst-output)不存在。

      验证这个程序工作正常。首先,输入命令:hadoop fs -ls MyFirst4-output

      [cloudera@quickstart ~]$ hadoop fs -ls MyFirst4-output

      Found 2 items

      -rw-r–r– 1 cloudera cloudera 0 2014-11-30 09:23 MyFirst4-output/_SUCCESS

      -rw-r–r– 1 cloudera cloudera 880829 2014-11-30 09:23 MyFirst4-output/part-00000

      然后,查看输出文件:

      [cloudera@quickstart ~]$ hadoop fs -cat MyFirst4-output/part-00000

      将文件从HDFS中拷入到你本地文件系统中:

      [cloudera@quickstart ~]$ hadoop fs -copyToLocal MyFirst4-output/part-00000

      MyFirstOutputLocal.txt

      现在,一切都准备好了,我们终于可以在Hadoop集群上运行我们的Python MapReduce作业了。如上所述,我们使用Hadoop流API通过STDIN和STDOUT在Map和Reduce间传递数据。
      这里写图片描述
    如果你想要在运行的时候修改Hadoop参数,如增加Reduce任务的数量,你可以使用-D选项:

      hduser@ubuntu:/usr/local/hadoop$ bin/hadoop jar contrib/streaming/hadoop-streaming.jar -D mapred.reduce.tasks=16 …

      关于mapred.map.tasks说明:Hadoop does not honor mapred.map.tasks beyond considering it a hint。但是,Hadoop接受用户指定mapred.reduce.tasks并且不操作。你不能强制指定mapred.map.tasks,但可以指定mapred.reduce.tasks。

      这个任务将读取HDFS目录/user/hduser/gutenberg中的所有文件,处理它们,并将结果存储在HDFS目录/user/hduser/gutenberg-output中。一般情况下,Hadoop对每个reducer产生一个输出文件;在我们的示例中,然而它将只创建单个文件因为输入的文件都很小。

      在终端中前一个命令的输出示例︰
    这里写图片描述
    使用Python语言写Hadoop MapReduce程序
      **译者说明:截图中的命令不完整,完整命令如下:

      hduser@ubuntu:/usr/local/hadoop$ bin/hadoop jar contrib/streaming/hadoop-streaming.jar -mapper /home/hduser/mapper.py -reducer /home/hduser/reducer.py -input /user/hduser/gutenberg/* -output /user/hduser/gutenberg-output

    展开全文
  • 到最后肯定想把完成的程序导出来,别人玩玩,都知道Java是跨平台的语言,但是总不能你把Java程序发给别人来运行吧。怎么转化为exe可执行的文件呢,所以这篇博客来一步一步演示。博主从刚开始学Java swing,就想写...

    学习Java的人,一定接触过Java swing 写过小界面程序比如 :计算器,MP3,批处理脚本程序,聊天小系统。。。到最后肯定想把完成的程序导出来,让别人玩玩,都知道Java是跨平台的语言,但是总不能你把Java程序发给别人来运行吧。怎么转化为exe可执行的文件呢,所以这篇博客来一步一步演示。博主从刚开始学Java swing,就想写个自己的计算器,到后面计算器写出来,一直尝试不知道怎么搞成exe的,一直没成功,直到今天,老大给分配个任务,给他写个批处理脚本,带界面的。功能两个小时写完了,研究的一下午终于成功了。在这里记录下。以供大家参考。

    我使用的工具是exe4j_windows-x64_5_0_1这是国外付费的软件,要激活,不激活也可以使用,这样有一个问题,你转换完成exe后,启动的时候总是会弹出一下界面:

    这里写图片描述

    鉴于国内的软件市场,很容易找到破解版的,注册机之类的东东。我已经将我使用的工具上传到csdn上了(带注册机!!!!)exe4j下载地址

    安装完后,打开注册机

    这里写图片描述

    生成license

    这里写图片描述

    这里写图片描述

    这些都so easy!!!


    让大家看看我写的批处理maven pom.xml小程序(老大不想一个一个更改版本号,大概有100多个pom.xml文件 )

    jar文件和转换后的exe文件

    这里写图片描述

    运行后的效果

    这里写图片描述


    废话少说!!!!


    选中你的项目右键export

    这里写图片描述

    选择 runnable jar

    (这里要说明下jar file和runnable jar file都可以导出,jar file只能通过java -jar filename命令来执行,不能通过双击来运行:就是 Java(TM)platform SE binary如下图 )

    这里写图片描述

    这里写图片描述

    一定要选择runnable jar file
    一定要选择runnable jar file
    一定要选择runnable jar file

    这里写图片描述

    这一步的详细说明:

    • “Launch configuration”下面的下拉列表中选择程序启动时运行的主类;
    • “Export destination”下面的“Browse”按钮选择JAR的存放路径,并输入JAR文件名;
    • “Library handling”如果程序依赖于其它包,选择“Package required libraries into generated JAR”或“Copy required libraries into a sub-folder next to the generated JAR”,如果选择后者,则导出时,会将需要的包拷贝到一个目录中,后面引用这个JAR包时,必须将这个目录拷到JAR包所在的目录。

    • (1)Extract required libraries into generated JAR。
      把所有的import JAR都拆开来,包含在JAR的各个目录中,ex. net/org/xxx.class

    • (2)Package required libraries into generated JAR。
      把所有的import JAR都包在JAR的根目录

    • (3)Copy required libraries into a sub-folder next to the generated JAR。
      把所有import JAR放在JAR外面独立的一个文件夹

    这三个我都测试过没什么影响,只是导入的包的位置不同。

    找到导入的jar文件,双击就可以运行了。


    终于到了exe4j上场的时刻。。。。。

    一共10步
    1-welcome 直接-next
    2-project-type 选择jar in exe 然后next
    这里写图片描述
    3-输入名字和目录
    这里写图片描述

    4- configure executable

    这里写图片描述
    4.1
    这里写图片描述
    4.2 -4.3 直接next

    4.4
    这里写图片描述

    5

    这里写图片描述

    5.1
    这里写图片描述

    6
    这里写图片描述

    这样的一个目录结构,当我们发给别人使用的时候直接打包Test文件,他解压之后打开test.exe这样就可以使用了。这里还有一点我们的jre最好通过右下角的箭头移到最上面,这样避免别人电脑自带jre出现不必要的麻烦。

    这里写图片描述

    接下来直接next就行了!!!!


    你的打赏是对我的认可

    这里写图片描述

    展开全文
  • 程序运行时,对象是怎么进行放置安排的呢?特别是内存是怎样分配的呢?对这些方面的了解会对你有很大的帮助。有六个不同的地方可以存储数据。 1) 寄存器 这是最快的存储区,因为它位于不同于其他存储区的地方——...
  • 由于公司业务需求原因,我一个做安卓app的来做一个windows桌面应用程序,由于对JAVA熟,项目周期紧,所有就没有考虑别的语言。然后前进的道路并没想象中那么顺畅,找了不少帖子,用了三款打包工具(1,exe4j;2,...
  • 现在的程序能够实现通每隔200ms小蛇自动往前跑一格,通过键盘指令能够改变方向,但是我不想它自动跑,只想实现给它指令它才移动,请问怎么实现? 这是蛇的活动类 ``` package com.tcs.cn; import java.awt....
  • 昨天晚上还是运行的好好的程序,今天中午来了以后就一直报上面那个错误,当然下面还有一大堆连看也看不懂的错误。试了好多遍,重新启动tomcat,试着用不同的浏览器,可是都是无济于事,上网查了一下,也不知道...
  • 我们的程序运行在solaris上的,由于我没下载到optimizeit的solaris安装文件,或者是类似工具的solaris安装文件,因此不确定,olc区中的那些对象是否就是那几个不断增长的对象。我用jvmstat看了,old区涨的很快。...
  • Python不用IDE怎么自己debug呢! java 有 jdb c++ 有 gdb python 就是 pdb啊 指令: python3 -m pdb test.py 进入debug模式后,下面是一些在pdb常用的debug指令: ...(continue)让程序一直运行到设置的断点 c...
  • JS 构造器的研究

    2008-03-12 12:05:46
    我们写的JAVA代码都是以类来封装,一个JAVA程序就是在不停从类模版中获取数据表示形式然后让进内存. 然而JAVASCRIPT却是直接解析语言,所有都是直接构建在代码之上.所以就没有所谓的模版之说了. 那怎么让我们的写的...
  • 内存泄漏

    2018-05-22 19:51:10
    Java 内存分配策略Java 程序运行时的内存分配策略有三种,分别是静态分配,栈式分配,和堆式分配,对应的,三种存储策略使用的内存空间主要分别是静态存储区(也称方法区)、栈区和堆区。静态存储区(方法区):主要...
  • java做个后台运行程序一直监视报警文件(或数据库),若有新记录,则读取然后发送到远程连接的客户端上。 想知道到底这个怎么实现,没有完整思路。这个实现,意思是监视是否有新的图片,如果有,就想办法...
  • 知道计算机是怎么工作的很重要,你的代码,程序怎么转换成计算机可以懂的语言,CPU的调度原理,内存工作原理等等。 java学习之路 有了上面的基础,终于我们可以开始讲java的学习之路了。 1. 工具 1.1 开发工具 ...
  • 新版Android开发教程.rar

    千次下载 热门讨论 2010-12-14 15:49:11
    程序可以采用 JAVA 开发,但是因为它的虚拟机 (Virtual Machine) Dalvik ,是将 JAVA 的 bytecode 转成 自 己的格式,回避掉需要付给 SUN 有关 JAVA 的授权费用。 对手机制造者的影响 � Android 是款开源的移动计算...
  • 内存管理的目的就是我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题。内存泄漏大家都不陌生了,简单粗俗的讲,就是该被释放的对象没有释放,一直被某个或某些实例所持有却不再被使用导致 GC 不能回收 我...
  • 内存管理的目的就是我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题。内存泄漏大家都不陌生了,简单粗俗的讲,就是该被释放的对象没有释放,一直被某个或某些实例所持有却不再被使用导致 GC 不能回收 我...
  • 广州南沙软件园面试试题 (C++部分)要求15分钟完成。1、简述ICQ或OICQ的工作原理。2、WINDOWS下C++如何申请内存?...6、有什么办法可以一个程序一直运行并能够处理多个用户的login和logout操作?7、typedef struct{
  • 多线程读取数据库

    2009-06-26 16:15:51
    这几天看别人用C#写一个多线程抓取,感觉运行起来比Java快,C#有个HttpWebRequest组件封装挺好的,感觉比java的好用,不知是否理解正确。我也打算用hibernate试试,不知道多线程下hibernate有没有要注意的问题。分...
  • 我很庆幸自己之前已经比较熟练的使用VC和Java了,整个C#入门用了不到半个小时,而从初次接触C#到开发这个QQ自动登录器也只用了短短的几个小时,下面,我就给大家讲讲如何用C#开发一个QQ自动登录器,大家真正体会到...
  • c#学习笔记.txt

    2008-12-15 14:01:21
    很多人觉得它应该像C或者C++,但事实上它更像是java的一个clone,所以作为入门,读一下清华大学出版社出版的《Java 语言与面向对象程序设计》可能会对你有所帮助。本文假定你具备一切学习此语言所需的知识,没有也不...
  • C#微软培训教材(高清PDF)

    千次下载 热门讨论 2009-07-30 08:51:17
    2.2 公用语言运行时环境与公用语言规范.13 2.3 开 发 工 具 .17 2.4 小 结 .19 第三章 编写第一个应用程序 .20 3.1 Welcome 程序 .20 3.2 代 码 分 析 .20 3.3 运 行 程 序 .23 .4 添 加 注 释 .25 ...

空空如也

空空如也

1 2 3 4 5 ... 7
收藏数 125
精华内容 50
关键字:

怎么让java程序一直运行

java 订阅