(05)基础强化:字符串拘留池,格式化,StringBuilder,垃圾回收,弱引用

news/2024/4/15 7:29:17

    
    
    
一、复习


    1.什么是接口?说说你对接口的理解。
    
        (提示:概念、语法、应用场景,与抽象类的区别。说出最特别的)
        接口是一种规范、标准,一种抽象的概念,所以本身无法实现,定义时用interface,
        只能有方法(属性、方法、索引器等)且不能有方法体。不能有字段等数据,且定义
        时不能用访问修饰符(隐式为public)。接口必须在继承中实现(除非是抽象方法),
        可以多继承。
            接口与抽象类都用于多态。都必须在继承中实现。但抽象类主要是同类中且只
        只能单继承。但接口可以不同类中实现多态(表示一种行为特征,如行为、能力等),
        且可以多继承。多个继承时,类名写在最前,后面接多个接口。如果类中有与接口
        同名的方法,可以使用显式定义接口(无修饰符)。
            结构也可以实现接口,但结构不能继承。
            
            
    2、说说try-catch的注意点?
        
        (提示:语法结构,finally,返回值)
        try-catch-finally,其中catch与finally必须至少有一个与try配对。try块中有异常
        则其后续代码不再执行,跳到catch中执行。catch可以有多个,只要有一个满足条件
        其它就不执行了。有finally时无论异常否必须执行,且在try与catch中可以有return,
        但finally中不能有return,返回前也必须执行finally.
            另外因为有返回值的方法都会创造一个临时变量存储返回值,因此当把return
        写入try或catch中时,小心返回值的变化。
        
        
    3、静态方法能被重载或重写吗?
        
        答:静态方法当类加载时就被加载到内存中,一直保持不变,直到程序退出。所以它
            是不能被重写的。
            而非静态方法是在对象实例化时,才单独申请内存空间,为每一个实例分配独立
        的运行内存,因而可以重写。
            
            静态方法与非静态方法都可以被重载,因为重载是在编译器编译时发生,与运行
        时无关。
            简言之:重载是编译时多态,重写是运行时多态。
            
            引申前面的:object.ReferenceEquals()不能被重写或重载。
        因为这是静态方法,所以不能被重写。另外不能人为进入object中去修改源代码进行
        重载(这是微软的内部领域)
        
        


二、常用类库String


    学习.net就是学习它的无数个类库怎么用.
    
    1、字符串的一些特性:
    
        string类型不能被继承(sealed关键字):密封类
        
        (1)sealed有两种用法:
        
                1)类名前加sealed,表示此类不能向下继承。
                2)方法重写时添加sealed,表示此方法不再向下进行重写。到此为止。

            internal class Program{private static void Main(string[] args){Person p = new C2();p.SayHi();Console.ReadKey();}}internal class Person{public virtual void SayHi(){Console.WriteLine("基类");}}internal class C1 : Person{public override sealed void SayHi(){Console.WriteLine("子类1");}}internal class C2 : C1{public override void SayHi()//出错,因为前面已经sealed,不能向下再重写.{Console.WriteLine("孙类2");}}


            
            思考:
                为什么string类型不允许继承(sealed)?(后面答案)
        
        
        
        (2)不可变性  (ToUpper演示,查看string类代码,只读索引器),
        
                字符串的任一方法返回的都是一个新的副本,其本身未变,因为它是不可变的。

            private static void Main(string[] args){string s1 = "Hello World";string s2 = s1.ToUpper();Console.WriteLine(s1 + "---" + s2);//本身未变Console.ReadKey();}

         结论:
                字符串一旦被创建,永远不可更改。
                

            private static void Main(string[] args){string s1 = "abc";string s2 = "x";s1 = s1 + s2;Console.WriteLine(s1 + "---" + s2);Console.ReadKey();}


            注意:
                s1发生变化。原因:堆中原"abc"未变。堆中新创建了"abcx",其地址赋值
            给了栈中的s1,所以s1指向了新的堆中地址。(原abc无人指向,但不会被回收)
            

            private static void Main(string[] args){string a = "a";string b = "b";string c = "c";a = a + b;a = a + c;Console.WriteLine(a);Console.ReadKey();}


            上面在堆中开辟了5块空间,也就是创建了5个对象。
            下面输出的结果相同,且开辟的空间和对象也是5个。

            private static void Main(string[] args){string a = "a";a = a + "b";a=a+"c";Console.WriteLine(a);Console.ReadKey();}


            
            可以看到字符串的拼接,造成了大量的内存浪费,每次创建字符串都会浪费资源。
            这都是字符串的不可变性造成的资源浪费。
        
        
        (3)字符串暂存池(拘留池)
        
                "abc",与控制台输入的"abc",与"a"+"b"+"c",与三个变量abe相加是否为同一
            个对象?(以此说明只针对常量。)

            string s1 = new string(new char[] { 'a', 'b', 'c' });string s2 = new string(new char[] { 'a', 'b', 'c' });s1 = s2;


            s1与s2是同一个对象.
            
            查看下面两种情况反编译情况:变量连接情况:

            private static void Main(string[] args){string s1= "abc";string a = "a", b = "b", c = "c";string s2 = a + b + c;Console.WriteLine(s2);Console.ReadKey();}


            反编译如
            

 


            
            因此可以看出最后是三个字符串Concat,因此是新创建变量,s1与s2是不同的。

            private static void Main(string[] args){string s1 = "abc";string a = "a", b = "b", c = "c";string s2 = "a" + "b" + "c";Console.WriteLine(s2);Console.ReadKey();}


            反编译后如:

 

 
            因此,看出实际,就是直接指向前面的s1,未创建新的变量,s1与s2是相同的。
            
            原因:
                针对字符串常量,建立一个缓存池,把常量字符串,建立一张在堆中的索引
            表。内部维护一个哈希表:key为字符串,value是地址。每次为一个新变量赋值
            都会找key中是否有,如果有则直接把value中的地址赋值给新变量。
                这样只要有,就直接返回地址,便于快速创建提高效率
                依赖于字符串的"不可变性",在整个程序的生命期中,该拘留池并不会消失
            也不会发生变量,只会在程序退出后释放,所以它会一直霸占堆中内存。
            
            1]、为什么只常量,不是变量?
                因为它的霸占,只适用于少量多次使用的情况。如果再加入众多的变量情况
            会造成堆中大量内存被占用,拖累程序或使程序崩溃。
            
            2]、为什么要有字符串暂存池?
                暂存池(拘留池)如同缓存一样,建立表后,可以有效提高命中率,节省创
            建字符串对象的时间。
            
            3]、大量的字符串常量,如何避免进入拘留池?
                由于其不可变性,若用大量的字符串常量,会造成堆中内存无效浪费。这
            时可以尽量用变量来使用字符串,使其在变量生命期外自动释放。
                例如:2万的字符串常量,可以存储在文件中,用变量进行读取,当这个
            文件关闭后,就可以释放该变量,而不用拘留池学期驻留在堆内存中。
            
                
        (4)如何手动添加到拘留池?
            
            对于动态字符串本身在哈希表中没有,通过这种Intern可以添加到该哈希表中,
        目的为了提高性能。
        
        String.Intern(x): Intern方法使用暂存池来搜索与 str 值相等的字符串。如果存
                        在这样的字符串,则返回暂存池中它的引用。如果不存在,则向
                        暂存池添加对 str 的引用,然后返回该引用。
        String.lsintened(x): 此方法在暂存池中查找 str。如果已经将 str放入暂存池中
                        则返回对此实例的引用;否则返回nullNothingnullptrnull引用
        
        注意:
            可以手动添加到拘留池,但不能手动在拘留池中删除。
    
        
        (5)为什么字符串要添加sealed,即不允许继承?
            
            答:两个原因:
            1、子类若能继承,可能会对字符串类进行修改(改变字符串的特性如不可变性);
            2、CLR对字符串提供了各种特殊的操作,如果有很多类继承了字符串类,则CLR
                需要对更多的类型提供特殊操作。这样有可能降低性能。
                比如,不可变性,下面的众多子类也会如此处理。
    
    


三、字符串格式化


    1、格式化
    演示: string.Format("===0,30:c31====",3.1415926535384);
    说明:{索引[,对齐][:格式字符串]}
        索引: 表示引用的对象列表中的第n个对象参数
        对齐(可选): 设置宽度+对齐方式,该参数为带符号的整数。
                正数为右对齐(不能加正号),负数为左对齐。
                例如: {0,50}表示宽度为50,右对齐。{0,-50}表示宽度为50,左对齐。
        格式字符串:常见的有"数字格式"、"日期与时间格式"、自定义格式等。
                用"冒号"开始。例如,
                数字格式: C表示货币、D表示十进制数、E表示科学记数法、G表示常规...
                其中可以写成Cxx.xx表示精度,范围为0-99,例如c3表示保留3位小数。
                
                日期时间格式: d表示短日期,D表示长日期,f表示完整日期+短时间,F表
                示完整日期+长时间,t表示短时间模式...

        private static void Main(string[] args){string s = "abc";Console.WriteLine("01234567890123456789------");Console.WriteLine(s);Console.WriteLine("=={0,10}==", s);Console.WriteLine("=={0,-10}==", s);double n = 3.14567;Console.WriteLine();Console.WriteLine("01234567890123456789------");Console.WriteLine(n);Console.WriteLine("=={0,10:f2}==", n);Console.WriteLine("=={0,10:e2}==", n);DateTime d = DateTime.Now;Console.WriteLine();Console.WriteLine("01234567890123456789------");Console.WriteLine(d.ToString("yyyy-MM-dd HH:mm:ss"));Console.WriteLine(d.ToString("d"));Console.WriteLine(d.ToString("D"));Console.WriteLine(d.ToString("f"));Console.WriteLine(d.ToString("F"));Console.ReadKey();}

 


        
        注意:
            Console.WriteLine()没有返回值直接输出到控制台
            string.Format()有返回值,可以赋值给一个字符串。
    
    
    2、String字符串常用方法
    
        字符串可以看成字符数组,不可变特性
        (通过for循环,修改string中的元素,将失败! 提示为只读属性)。
        当输入时就会提示错误:
      

 


        
        用net Reflector查询一下string类中的索引器char[]的反编译情况:
      

 



        可以看到属性只有get,没有set,即只能读取,不能写入。
        
        
        属性
            Length //获得字符串中字符的个数。"aA我你他"->5
            
            
        方法
            IsNullOrEmpty() 静态方法判断为null或者为""(静态方法)
            ToCharArray()   将string转换为char[]
            ToLower()     小写,必须接收返回值。(因为: 字符串的不可变)
            ToUpper()     大写。
            Equals()     比较两个字符串是否相同。忽略大小写的比较,StringComparation.
            IndexOf()    如果没有找到对应的数据,返回-1.
            LastlndexOf()  如果没有找到对应的数据,返回-1
            Substring()    2个重载,截取字符串。
            Split()      分害字符串。
            Join()   静态方法
            Format   静态方法
            Replace()        
        
        
        怎么判断一个字符串是空字符串?
            null,空对象。堆中没有分配内存,变量在栈中存储的内容为0x000000,
                    即不指向堆中的任何地址。
            "",空字串。堆中已经分配了内存,只是内容为空。变量在栈中存储的内容就
                    是在堆中分配内存的地址。
            空串的判断:

            private static void Main(string[] args){string s = "";if (s == "")//方法一{}if (s == string.Empty)//方法二{}if (s.Length == 0)//方法三   推荐{}Console.ReadKey();}


            
            或者可能为null时:

            private static void Main(string[] args){string s = "";if (s == null || s.Length==0)//方法一{}if (string.IsNullOrEmpty(s))//方法二{}Console.ReadKey();}


            
            
        对两个字符串比较是否相同,忽略大小写。

        private static void Main(string[] args){string s1 = "abc";string s2 = "ABC";Console.WriteLine(s2.ToLower() == s1.ToLower());Console.WriteLine(s1.ToLower().Equals(s2.ToLower()));Console.WriteLine(s1.Equals(s2, StringComparison.OrdinalIgnoreCase));Console.ReadKey();}


        
        
        面试题: 统计一个字符串中,"天安门"出现的次数。

        private static void Main(string[] args){string s = "天安门中数天安门,天安门中有几个天安门?";int count = 0, i = 0;while (true){i = s.IndexOf("天安门", i);if (i == -1 || i > s.Length - 3)//未找到或已超过长度{break;}i += 3;//词的长度count++;//累加}Console.WriteLine(count);Console.ReadKey();}


    
    
        如何把char[]数组转为字符串string?
            不能直接用ToString,没有这个重载,只会输出类型。

        private static void Main(string[] args){char[] chars = new char[] { 'a', 'b', 'c' };string s1 = chars.ToString();Console.WriteLine(s1);//System.Char[]string s2 = new string(chars);Console.WriteLine(s2);//"abc"Console.ReadKey();}


        
        
        截取字符串。(从零开始计数)

        string s = "welcome to our country!!!";Console.WriteLine(s.Substring(11, 1));//our


        


四、字符串的处理练习

    1、接收用户输入的字符串,将其中的字符以与输入相反的顺序输出。"abc"->"cba".

        private static void Main(string[] args){Console.WriteLine("请输入字符串:");string s = Console.ReadLine();if (s.Length > 1){char[] c = s.ToCharArray();for (int i = 0; i < s.Length / 2; i++){char temp = c[i];c[i] = c[s.Length - 1 - i];c[s.Length - 1 - i] = temp;}s = new string(c);}Console.WriteLine(s);Console.ReadKey();}


    
    
    2、接收用户输入的一句英文,将其中的单词以反序输出。
        "I love you""l evol uoy"

        private static void Main(string[] args){Console.WriteLine("请输入一句英文:");string s = Console.ReadLine();s = s.Replace("\"", " \" ").Replace("?", " ? ").Replace(".", " . ").Replace(",", " , ");string[] s1 = s.Split(' ');for (int i = 0; i < s1.Length; i++){s1[i] = Reverse(s1[i]);}s = string.Join(" ", s1).Replace(" \" ", "\"").Replace(" ? ", "?").Replace(" . ", ".").Replace(" , ", ",");Console.WriteLine(s);Console.ReadKey();}private static string Reverse(string s){if (s.Length > 1){char[] c = s.ToCharArray();for (int i = 0; i < s.Length / 2; i++){char temp = c[i];c[i] = c[s.Length - 1 - i];c[s.Length - 1 - i] = temp;}s = new string(c);}return s;}


        结果:
        

 


    
    3、"2012年12月21日"从日期字符串中把年月日分别取出来,打印到控制台.

        private static void Main(string[] args){string s = "22012年1月1日";int i = 0, j = s.IndexOf("年");string year = s.Substring(0, j);i = s.IndexOf("月");string month = s.Substring(j + 1, i - j - 1);j = s.IndexOf("日");string day = s.Substring(i + 1, j - i - 1);Console.WriteLine(year + "-" + month + "-" + day);Console.ReadKey();}


    
    
    4、把csv文件中的联系人姓名和电话显示出来。简单模拟csv文件,csv文件就是使用
        分割数据的文本,输出:姓名: 张三 电话: 15001111113
        string[] lines = File.ReadAllLines("1.csv",Encoding.Default);
        //读取文件中的所有行,到数组中。        

        private static void Main(string[] args){string[] s = File.ReadAllLines(@"E:\csv.csv");for (int i = 0; i < s.Length; i++){string[] p = s[i].Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);Console.WriteLine($"姓名:{p[0]},电话:{p[1]}");}Console.ReadKey();}


        
        
    5、123-456--7--89---123--2把类似的字符串中重复符号"-"去掉,即得到
        123-456-7-89-123-2.
        split(),StringSplitOptions.RemoveEmptyEntries,Join()

        private static void Main(string[] args){string s = "123-456--7--89---123--2";string[] p = s.Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries);Console.WriteLine(string.Join("-", p));Console.ReadKey();}


    
    
    
    6、从文件路径中提取出文件名(包含后缀)。比如从c:\a\b.txt中提取出b.txt这个
        文件名出来。以后还会学更简单的方式:"正则表达式",项目中我们用微软提供
        的:Path.GetFileName();(更简单)

        private static void Main(string[] args){string s = @"c:\a\b.txt";string s1 = Path.GetFileName(s);string s2 = Path.GetDirectoryName(s);Console.WriteLine(s1);Console.WriteLine(s2);Console.ReadKey();}


    
    
    7、"192.168.10.5[port=21,type=ftp]",这个字符串表示IP地址为192.168.10.5的服务
        器的21端口提供的是ftp服务,其中如果",type=ftp"部分被省略,则默认为http服
        务。请用程序解析此字符串,然后打印出"IP地址为***的服务器的***端口提供的服
        务为***"
        line.Contains("type=")。192.168.10.5[port=21]

        private static void Main(string[] args){string s = "192.168.10.5[port=21,type=ftp]";string[] p = s.Split(new string[] { "[port=", ",type=", "]" }, StringSplitOptions.RemoveEmptyEntries);string type;if (p.Length == 2){type = "http";}else{type = p[2];}Console.WriteLine($"IP地址为{p[0]}的服务器的{p[1]}端口提供的服务为{type}");Console.ReadKey();}


    
    
    8、求员工工资文件中,员工的最高工资、最低工资、平均工资
        工资文件1.txt:
                张三=5000
                李四=6000
                王五=7000
                赵六=8000
                田七=5500
                孙八=7100

        private static void Main(string[] args){string[] s = File.ReadAllLines(@"E:\1.txt");double[] ds = new double[s.Length];for (int i = 0; i < s.Length; i++){ds[i] = Convert.ToDouble(s[i].Substring(s[i].IndexOf("=") + 1));}Console.WriteLine($"最大值{ds.Max()},最小值{ds.Min()},平均值{Math.Round(ds.Average(), 2)}");Console.ReadKey();}


        
    
    9、"北京传智播客软件培训,传智播客.net培训,传智播客Java培训。传智播客官网:
        http://www.itcast.cn。北京传智播客欢迎您。"。在以上字符串中请统计出
        "传智播客"出现的次数。找IndexOf()的重载        

        private static void Main(string[] args){string s = "北京传智播客软件培训,传智播客.net培训,传智播客Java培"+"训。传智播客官网:http://www.itcast.cn。北京传智播客欢迎您。";int count = 0, i = 0;List<int> list = new List<int>();while (true){i = s.IndexOf("传智播客", i);if (i == -1 || i > s.Length - 4){break;}list.Add(i);//索引位置i += 4;count++;}Console.WriteLine(count);Console.ReadKey();}


        

        private static void Main(string[] args){string s = "北京传智播客软件培训,传智播客.net培训,传智播客Java培" +"训。传智播客官网:http://www.itcast.cn。北京传智播客欢迎您。";int count = 0, index = 0;while ((index = s.IndexOf("传智播客", index)) != -1){count++;index += 4;}Console.WriteLine(count);Console.ReadKey();}


        


五、常用类库StringBuilder


    StringBuilder高效的字符串操作
        当大量进行字符串操作的时候,比如,很多次的字符串的拼接操作。
        String 对象是不可变的。每次使用 System.String 类中的一个方法时,都要
            在内存中创建一个新的字符串对象,这就需要为该新对象分配新的空间。
            在需要对字符串执行重复修改的情况下,与创建新的 String 对象相关的
            系统开销可能会非常大。
            
        如果要修改字符串而不创建新的对象,则可以使用System.Text.StringBuilder 类。
            例如,当在一个循环中将许多字符串连接在一起时,
                    使用StringBuilder 类可以提升性能。
            
            
    StringBuilder != String//将StringBuilder转换为 String.用ToString().
    StringBuilder仅仅是拼接字符串的工具,大多数情况下还需要把StringBuilder转换
        为 String.

        StringBuilder sb = new StringBuilder().sb.Append();//追加字符串sb.ToString();//把StringBuilder转换为字符串。sb.Insert();sb.Replace():


        
        


六、垃圾回收


    1、CLR的一个核心功能一垃圾回收
    
    
    2、垃圾回收的目的:
        提高内存利用率。
        (内存是有限的,程序中断地使用,若分配的变量都不回收,内存会越来越小,当
        不够用时,程序就会崩溃。)
        
        
    3、垃圾回收器,它回收什么?
        只回收托管堆中的内存资源,
        不回收其他资源(数据库连接、文件句柄、网络端口等)。
                    
        栈中的值类型资源,不需要垃圾回收。它们使用完成后会立即释放。
            
            
    4、什么样的对象才会被垃圾回收?
        没有变量引用的对象。没有变量引用的对象,表示可以被回收了(null)断了线的
            风筝,再也回不来了。
        大学食堂(自己收盘子)、大排档(不需要程序员自己收盘子)
        
        可以被回收,并不意味着它马上就被回收。
    
    
        问题1:下面运行到x处时p分配的空间是否可以被回收?

        internal class Program{private static void Main(string[] args){Person p = new Person();p.Name = "杨贵妃";Person p1 = p;p = null;//p1=null;Console.ReadKey();//x处}}internal class Person{public string Name { get; set; }}


        
        答:不可以。p虽然为空,但已经转交给p1,由p1指向该空间,该空间仍然在被p1
            使用。如果下一句有p1=null,这块内存再也不会有任何对象引用。那么这块
            内存如断线的风筝,再也没人引用指向。这时候是可以被回收。
            
            注意:可以被回收并不意味着马上被回收。
                什么时候回收,时间并不确定,这由GC决定。
        
        
        问题2:下面运行到x处时,p1,p2,p3是否可以被回收?

        internal class Program{private static void Main(string[] args){Person p1 = new Person();Person p2 = new Person();Person p3 = new Person();Person[] ps = new Person[] { p1, p2, p3 };p1 = null;p2 = null;p3 = null;Console.ReadKey();//x}}internal class Person{public string Name { get; set; }}


        答:不可以。p1,p2,p3解除了引用,但ps仍然在引用这块内存。
        
        
    5、垃圾回收GC在什么时间回收?
        不确定,当程序需要新内存的时候开始执行回收。
        
        Gc.Collect();
        //手动调用垃圾回收器。不建议使用,垃圾回收时会暂停一下(非常短暂)
        //让程序自动去GC。
        
        垃圾回收是.Net的CLR自动来执行,一般不需手动干预。
        正如食堂吃完后,无需收拾由阿姨来自动收拾。但你吃完非要喊一句"阿姨来收拾"
        这反而打乱了程序的自动回收流程,造成效率低下。
        
        什么时候必须进行手动回收垃圾呢?
            如果在即将进行了一大段非常重要的代码运行,并且预估可能使用大量的内
        存,不需要或者尽量不让垃圾回收,这时可以提前使用手动调用垃圾回收。可以
        释放出一些较多空余的内存,同时手动回收后,在这段代码运行期间再次被自动
        垃圾回收的机率就会变小,从而不会影响这段代码的效率。
            另外就是非托管的程序代码,它已经不在GC控制范围,所以必须手动显式地
        进行回收,否则会占用系统资源,甚至可能出现意想不到的错误。
        
        
    6、垃圾回收对程序有影响吗?
        垃圾回收时,程序会暂停,保存当前的运行状态,然后进行清理,清理结束后,
        恢复原来的运行状态,程序继续进行。
            所以垃圾回收是对程序有一些影响的,但这个影响已经优化到极限,一般
        不影响程序,人也无法感受到。除非对一些性能有苛刻要求的程序才会有影响。
        
            
    7、垃圾回收的过程是怎样的?
        如果垃圾回收每次都把整个堆全面搜索一次进行清理,那么它的效率明显就很
        低,还影响程序的运行。
            于是垃圾回收器使用"代"的概念,采用"代"的机制进行回收,大大提高了垃
        圾回收的效率。
            一共分3代: 第0代、第1代、第2代。

        private static void Main(string[] args){int n = GC.MaxGeneration;//垃圾回收支持的最大代数Console.WriteLine(n);//2    即0,1,2共三代GC.Collect(0);//只回收第0代GC.Collect(1);GC.Collect(2);GC.Collect();//回收所有代数Console.ReadKey();}


            
            各代的回收频率:第0代最高,其次第1代,再次第2代。
            每个代的都指定了一定的初始容量空间,来装载各代变量。
            申请变量首先进入第0代,直到占满第0代的初始规定容量时:
            先回收第0代,第0代回收后存活下来的,就进行第1代。如此往复,直到第0代
        存活下来的,试图再进入第1代时,发现第1代初始容量空间已经满了,则回收第1
        代。同时第1代回收后存活的,则进入第2代。
            如此重复,也就是说越老的对象生存几率越大,而新建的对象则最易被回收。
            第0代的就如士兵,炮灰,最易被回收;第0代的士兵存活下来的,去第1代当
        团长。一旦第1代的团长也满后,才去从回收第1代,从团长群中找存活的,进入
        第2代去当军长。
            如果第0-2代都满了,则会试着扩大0-2代各代的初始容量,以适应程序运行。
        如果一直扩大,都无法满足程序,则到达一限度后,程序就会抛出异常。
        
        
    8、.net中垃圾回收算法: 
            mark-and-compact(标记和压缩),一开始假设所有对象都是垃圾。
            
            首先确定所有可达对象,然后移动这些对象,使它们紧挨着存放,有点类似于
        磁盘碎片整理。
            一次垃圾回收周期开始时,垃圾回收器会识别出对象的所有根引用,然后遍历
        根引用所标识的树形结构,并递归确定所有根引用指向的对象,这样,垃圾回收器
        就识别出了所有可达对象。
            执行垃圾回收时,垃圾回收器将所有可达对象紧挨着放在一起,从而覆盖不可
        达对象所占用的内存。
            

 


            图中上部分是回收前(绿色在用,黄色为垃圾),回收后是下半部分。
            此算法的优点是不会导致内存碎片化,回收后内存分配更高效。
            缺点是:移动内存需要额外的开销,同时仍然要扫描整个内存空间。
            所以为了优化使用代的概念,避免扫描整个内存。
                9、除了内存资源外的其他资源怎么办?
        比如a方法中调用了系统中的方法。
        由于这些资源是非托管的,所以需要手动去释放。比如析构函数,或者Dispose()等。    
    
    
    10、为什么.net没有析构函数?
        析构函数的目的是释放资源,但已经有了GC,它会自动调用GC进行资源的释放。
        
        但,也可以人为写一下象C++中的"析构函数":

        internal class Program{private static void Main(string[] args){}}internal class Person{~Person(){}}


        上面生成后,用.Net Reflector反编译看一下:
      

 


        可以看到这个"析构函数",实际上被编译成终结器(终结函数,也即"析构函数")
            protected override void Finalize();
        当对象可以被回收,当GC回收时,它先执行这个终结器,再调用GC进行回收。所
        以一般非托管资源的释放,就放在Finalize()这个终结器中,以便克服GC不能回
        收非托管资源的缺陷。
            但这个有一个缺点
            尽管可以被回收,但并不能马上被回收,这个要等GC来回收时,才先执行这
        个终结器,造成非托管资资源一直被占用。
             简言之:程序员不能控制析构器何时将被执行因为这是由垃圾收集器决定的。
            
            为了克服这个缺点,使用一个叫IDispose的接口,它是所有处理所有非托管
        资源的释放方法,需要人为在对象中重新实现,可以直接手动调用马上释放。而
        不必用析构函数或终结器(它需要等待GC的到来)。

        internal class Program{private static void Main(string[] args){Person p = new Person();p.Name = "晋文公";p.Dispose();//手动立即释放,不必等GCConsole.ReadKey();}}internal class Person : IDisposable{public string Name { get; set; }//~Person()//不推荐,不使用//{//}public void Dispose(){//这里人为手动实现,在不用对象时,直接调用该方法可立即释放Console.WriteLine("Dispose()");}}


    
        上面代码另一个有效释放就是用using语句,主程序改为:

        private static void Main(string[] args){using (Person p = new Person()){p.Name = "KO";}Console.ReadKey();}


        using()是使用非托管资源的最佳方式,可以确保资源在代码块结束之后被正确释放,
        并且代码更简洁。
        
        这里说的非托管资源指的是实现IDisposable或IAsyncDisposable接口的类。
        using实际上是一个try-finally,在finally会自动调用dipose,即无论执行正常与
        否,最终都会有非托管资源的释放。
            当代码离开using语句块,就会自动调用声明的非托管变量中dispose.


 七、弱引用    
  


    当一个对象在堆中没有任何人引用时,它就是一个断线的风筝,它就可以被垃圾回收。
    
    但有时我们程序中动态中可能会再次使用它,但又不清楚它是否已经被垃圾回收了。
    
    此时,在这个对象(风筝)断线(null)之前,就设置一个"标志"(弱引用)。
    
    在下次可能再使用前,进行该对象的引用或判断。
    
    1、为什么要使用弱引用?
            因为创建一个对象,特别是一个大的对象,特别费资源。弱引用能很大程序
        上免除重新创建一个新对象,而是直接使用原来的对象,节省资源。
        
    
    2、弱引用的注意事项
        
        弱引用类为WeakReference,主要有:
        IsAlive:判断是否存活。
                注意可能判断时还存活,但下一瞬间就被回收了。
        Target: 目标对象(即原对象)。
                利用此属性,可将当前对象进行转换。

        internal class Program{private static void Main(string[] args){Person p = new Person();p.Name = "朱元彰";WeakReference wr = new WeakReference(p);//声明弱引用p = null;方法一:不推荐//if (wr.IsAlive)//{//    object o = wr.Target;//    if (o != null)//    {//        Person p1 = o as Person;//        //p活了(即p1),又可以用了//    }//}方法一:啰嗦//if (wr.Target != null)//{//    object o = wr.Target;//    Person p1 = o as Person;//    //p1又活了,又可以用了//}//方法三:推荐Person p1 = wr.Target as Person;if (p1 == null){//重新创建p1}else{//p活了为p1,又可以使用了。}Console.ReadKey();}}internal class Person{public string Name { get; set; }}


        方法一:在IsAlive判断后的瞬间,可能被GC回收,所以代码本身就有bug。
        方法二:过程比较啰嗦。
        方法三:推荐写法。直接提取转换,为null就新建,否则直接使用。
        
        
    3、弱引用的工作原理
        引用https://www.cnblogs.com/johnyang/p/17205466.html
        
            弱引用保持的是一个GC"不可见"的引用,是指弱引用不会增加对象的引用计数,
        也不会阻止垃圾回收器对该对象进行回收。因此,弱引用的目标对象可以被垃圾回
        收器回收,而弱引用本身不会对垃圾回收造成任何影响。

            弱引用的原理是,在堆上分配的每个对象都有一个头部信息,用于存储对象的
        类型信息、对象的大小等信息。在头部信息中,还会有一个标志位用于表示对象是
        否被引用。
        
            当一个对象被创建时,该标志位为"未引用"。当该对象被弱引用引用时,该标
        志位不会变为"已引用",即该对象仍然会被当做未引用的对象进行处理。
        
            被强引用后,会被标记为"已引用",当所有的强引用都消失时,该标志位会变
        为"未引用",即该对象已经没有任何强引用指向它,标记的工作由GC来完成。

            在垃圾回收时,GC会根据标记-清除算法对堆中的对象进行扫描和标记,标记
        所有仍然被引用的对象,然后回收所有未被标记的对象。
        
            对于被弱引用引用的对象,由于弱引用不会增加对象的引用计数,也不会阻止
        垃圾回收器回收该对象,因此在回收时,该对象会被当做未被引用的对象进行处
        理,然后被回收。            总之,弱引用保持的是一个GC"不可见"的引用,即弱引用不会影响垃圾回收器
        对目标对象的回收,因此可以用于实现一些场景,例如缓存、对象池等场景,避免
        长时间占用内存或造成内存泄漏。

        private static void Main(string[] args){var sb = new StringBuilder("weak");var weak = new WeakReference(sb);Console.WriteLine("before GC");Console.WriteLine(weak.Target);GC.Collect();Console.WriteLine("after GC");if (weak.Target == null){Console.WriteLine("now it has been cleared...");}else{Console.WriteLine(weak.Target);}Console.ReadKey();}


        
         在vs2022上工具栏下选择Debug/Release两种模式分别生成可执行文件。在对应的
         项目的子目录中的Debug或Release中分别生成exe文件,分别执行那么结果不同:
        1)Debug下:
                before GC
                weak
                after GC
                weak
        2)Release下:
                before GC
                weak
                after GC
                now it has been cleared...
                
            在 debug 模式下,GC.Collect 方法仍然会工作,但是它的行为可能会受到
        一些影响。

            在 debug 模式下,编译器会添加额外的调试信息到代码中,这些信息可能会
        影响垃圾回收器的行为。例如,编译器可能会保留一些对象的引用,以便调试器
        可以访问它们,这可能会导致这些对象不会被垃圾回收器回收,直到调试器不再
        需要它们为止。
            因此,当调用 GC.Collect 方法时,由于存在调试信息的影响,可能会出现
        一些对象无法被立即回收的情况。

            此外,在 debug 模式下,垃圾回收器的性能也可能会受到一定的影响,因
        为编译器会添加额外的代码和调试信息,导致程序变得更加复杂和庞大,从而使
        垃圾回收器需要更长的时间来扫描和回收对象。

            因此,如果需要在 debug 模式下进行垃圾回收操作,应该仔细考虑其影响,
        并进行充分的测试,以确保程序的正确性和性能。
            同时,还可以考虑使用其他的调试工具和技术来诊断和解决问题,避免对
        程序的垃圾回收行为产生不必要的影响。
        
        
 


http://www.ppmy.cn/news/58013.html

相关文章

关于对于springcloud中的注册中心和consume消费者和provier服务者之间的关系理解

关于对于springcloud中的注册中心和consume消费者和provier服务者之间的关系理解 pringCloud provider&#xff08;服务提供方&#xff09; consumer&#xff08;服务调用方&#xff09; server&#xff08;注册中心&#xff09; 运行原理 Provider 第一步 provider注册到se…

Ansible的脚本-playbook 剧本

目录 1.剧本&#xff08;playbook&#xff09; 1.playbook介绍 2. playbooks 的组成 3.案例&#xff1a;编写httpd的playbook 4.定义、引用变量 5.指定远程主机sudo切换用户 6.when条件判断 7.迭代 2.playbook的模块 1.Templates 模块 2.tags 模块 3.Roles 模块 1.…

SpringBoot定义优雅全局统一Restful API 响应框架二

这里解决之前留下来的问题&#xff0c;当程序没有正常返回时候 就是程序由于运行时异常导致的结果&#xff0c;有些异常我们可&#xff0c;能无法提前预知&#xff0c;不能正常走到我们return的R对象返回。这个时候该如何处理 在SpringBoot中&#xff0c;可以使用ControllerA…

ctfshow之_萌新web1至web7

一、访问在线靶场ctfshow ctf.showhttps://ctf.show/challenges如下图所示&#xff0c;进入_萌新赛的web1问题&#xff1a; 如上图所示&#xff0c;页面代码提示id1000时&#xff0c;可以查询到flag&#xff0c;进行如下尝试&#xff1a; 如下图所示&#xff0c;传入参数id1时…

DAY 47 Ngnix优化与防盗链

Ngnix优化主要有两种&#xff0c;一种是配置上的优化&#xff0c;一种是内核上的优化 隐藏响应头中的版本号 方法一&#xff1a;curl命令 网页查看 隐藏版本信息 修改nginx的运行用户和组 方法一&#xff1a;在编译安装时&#xff0c;指定运行用户和组 [root nginx-1.12.2]#…

JoJo‘s Incredible Adventures

题目&#xff1a; 题意解析&#xff1a; 这个题目是要求找出输入的字符串&#xff0c;&#xff0c;字符串的循环移位s由k右边是字符串Sn−k1...Sn&#xff0c;S1&#xff0c;S2...Sn−k。直到所有的字符&#xff0c;都循坏出现在字符串的开头&#xff0c;然后输入1形成的长方形…

「SQL面试题库」 No_55 销售分析 I

&#x1f345; 1、专栏介绍 「SQL面试题库」是由 不是西红柿 发起&#xff0c;全员免费参与的SQL学习活动。我每天发布1道SQL面试真题&#xff0c;从简单到困难&#xff0c;涵盖所有SQL知识点&#xff0c;我敢保证只要做完这100道题&#xff0c;不仅能轻松搞定面试&#xff0…

【云原生Kubernetes】01-Kubernetes简介

【云原生Kubernetes】01-Kubernetes简介 文章目录 【云原生Kubernetes】01-Kubernetes简介前言kubernets概述为什么要使用Kubernetes?Kubernetes能做什么&#xff1f;Kubenets架构架构图架构组件说明Master节点Node节点Etcd节点 组件间的工作流程 Kubernetes的核心技术Pod副本…

Java Web应用开发 ——第三章:JSP内置对象测试

一.单项选择题&#xff08;共15题,60.0分&#xff09; 1 使用response对象进行重定向时&#xff0c;使用的方法是&#xff08; A、 setAttribute B、 sendRedirect C、 setContentType D、 getAttribute 正确答案&#xff1a; B 2 session对象中用于设定指定名字的属性值…

ElasticSearch(二)简介

1. 简介 Elasticsearch 是一个分布式、高扩展、高实时的搜索与数据分析引擎。 它能很方便的使大量数据具有搜索、分析和探索的能力。充分利用Elasticsearch的水平伸缩性&#xff0c;能使数据在生产环境变得更有价值。 Elasticsearch 的实现原理主要分为以下几个步骤&#xf…

@RestControllerAdvice注解

目录 1. RestControllerAdvice注解 详解&#xff1a; 1.1 概述 1.2 用途&#xff1a; 1.3 基本使用&#xff1a; 1.4 属性&#xff1a; annotations: basePackages: basePackageClasses: assignableTypes: 1.5 与ExceptionHandler的结合&#xff1a; 1.6 总结 2. R…

S3C6410 中的 cascaded irqdomain 之 gpio

文章目录 VIC 中断 与 gpio 中断 的硬件拓扑图描述linux cascaded irq domainirq domain 初始化时获取 IRQ number(软件中断号) 时中断发生时如何调试linux irq domain 实例 VIC domain 与 gpio domain 的硬件拓扑语言描述VIC 与 INT_EINTx 的关系INT_EINTx 与 GPIO的关系INT_E…

回炉重造十一------ansible批量安装服务

1.playbook的核心组件 Hosts 执行的远程主机列表Tasks 任务集,由多个task的元素组成的列表实现,每个task是一个字典,一个完整的代码块功能需最 少元素需包括 name 和 task,一个name只能包括一个taskVariables 内置变量或自定义变量在playbook中调用Templates 模板&#xff0c;…

springboot第15集:MyBatis分页

我们在测试SQL的时候&#xff0c;要是能够在控制台输出 SQL 的话&#xff0c;是不是就能够有更快的排错效率&#xff1f; 是的&#xff0c;输出 SQL 可以帮助我们更好地理解代码的执行流程和结果。在控制台输出 SQL 可以让我们看到实际执行的 SQL 语句&#xff0c;这样就能够更…

Scalable Vector Graphics (SVG)中的svg、clipPath、mask元素

Scalable Vector Graphics (SVG)是一种用于描述二维向量图形的XML基础标记语言。使用SVG可以实现丰富的图形效果&#xff0c;而不需要像使用位图那样考虑分辨率和像素密度的问题&#xff0c;可以在不同设备上展示出相同的高质量图像。 在SVG中&#xff0c;除了基本形状如circl…

【人工智能】— 不确定性、先验概率/后验概率、概率密度、贝叶斯法则、朴素贝叶斯 、最大似然估计

【人工智能】— 不确定性 不确定性不确定性与理性决策基本概率符号先验概率(无条件概率)/后验概率(条件概率)随机变量概率密度联合概率分布公理完全联合分布概率演算独立性 贝叶斯法则例1例2 使用贝叶斯规则&#xff1a;合并证据朴素贝叶斯最大似然估计小结 不确定性 不确定性与…

码出高效:Java开发手册笔记(线程池及其源码)

码出高效&#xff1a;Java开发手册笔记&#xff08;线程池及其源码&#xff09; 码出高效&#xff1a;Java开发手册笔记&#xff08;线程池及其源码&#xff09; 码出高效&#xff1a;Java开发手册笔记&#xff08;线程池及其源码&#xff09;前言一、线程池的作用线程的生命周…

Sentinel源码分析学习

文章目录 前言Sentinel源码分析1.Sentinel的基本概念1.1.ProcessorSlotChain1.2.Node1.3.Entry1.3.1.自定义资源1.3.2.基于注解标记资源 1.4.Context1.4.1.什么是Context1.4.2.Context的初始化1.4.2.1.自动装配1.4.2.2.AbstractSentinelInterceptor1.4.2.3.ContextUtil 2.Proce…

【网络进阶】服务器模型Reactor与Proactor

文章目录 1. Reactor模型2. Proactor模型3. 同步IO模拟Proactor模型 在高并发编程和网络连接的消息处理中&#xff0c;通常可分为两个阶段&#xff1a;等待消息就绪和消息处理。当使用默认的阻塞套接字时&#xff08;例如每个线程专门处理一个连接&#xff09;&#xff0c;这两…

clion--cmake内置常量---打算一锅端了

# cmakelists.txt文件讲解&#xff0c;以下语意不是都在同一个cmakelists.txt message("${CMAKE_SOURCE_DIR} 是工程终极父级目录") message("${CMAKE_BINARY_DIR} 是CMAKE_SOURCE_DIR下的一个cmake_build_<buildtype>目录") message("${PROJEC…
最新文章