.
목 차
확인 문제
using System;
public class Logger
{
public string LogMessages { get; private set; }
public Logger()
{
LogMessages = string.Empty;
}
public void Log(string message)
{
LogMessages += message + "\n";
}
}
public class Program
{
public static void Main()
{
Logger logger = new Logger();
for (int i = 0; i < 10000; i++)
{
logger.Log("This is log message number " + i);
}
Console.WriteLine("Logging completed. Total log length: " + logger.LogMessages.Length);
}
}
1. 위의 코드가 문제가 되는 이유를 메모리 관점에서 설명해주세요.
string 자료형은 기본적으로 불변한다. 즉, 한 번 생성된 문자열 객체는 변경할 수 없다. 하지만 어떤 변수를 string형으로 선언하고 바꿀 수 있다. 이는 문자열의 내용을 수정할 때마다 새로운 문자열 객체가 생성되고 기존 객체를 메모리에서 해제해주는 과정을 해줌으로써 가능해진다. 따라서 string의 값을 수정하게 된다면 매번 새 객체가 생성되어 메모리가 낭비된다.
2. 아래와 같이 string이 아닌 StringBuilder가 권장되는 이유는 무엇일까요?
public class Logger
{
private StringBuilder logMessages;
public Logger()
{
logMessages = new StringBuilder();
}
public void Log(string message)
{
logMessages.Append(message).Append("\n");
}
public override string ToString()
{
return logMessages.ToString();
}
}
위에서 언급했듯이, 문자열을 수정하면 객체의 메모리 해제와 생성이 반복되며 메모리 낭비가 발생한다. StringBuilder를 사용하면 이 단점을 상쇄할 수 있다.
설명 문제
1. 가비지 컬렉터란 무엇인가요?
가비지 컬렉터는 프로그래밍 언어에서 더 이상 사용되지 않는 메모리 영역을 자동으로 해제하여 프로그램의 메모리 관리를 돕는 시스템입니다. 이는 메모리 누수를 방지하고, 프로그래머가 직접 메모리 관리에 신경 쓰지 않도록 도와줍니다. 대표적으로 Java, Python, C# 같은 언어에서 가비지 컬렉터를 사용합니다.
2. 가비지 컬렉터의 장점과 단점에 대해 설명해주세요.
장점:
- 자동 메모리 관리: 프로그래머가 메모리 해제에 신경 쓰지 않아도 되어 코드가 간결해지고 오류가 줄어듭니다.
- 메모리 누수 방지: 더 이상 사용되지 않는 객체를 자동으로 제거하여 메모리 누수를 방지합니다.
- 안전성: 잘못된 메모리 접근으로 인한 오류를 줄여줍니다.
단점:
- 성능 저하: 가비지 컬렉션 과정에서 일시적으로 애플리케이션의 성능이 저하될 수 있습니다.
- 예측 불가능한 타이밍: 가비지 컬렉션이 언제 일어날지 예측하기 어려워 실시간 응답이 중요한 시스템에서는 문제가 될 수 있습니다.
- 추가 메모리 사용: 가비지 컬렉터 자체가 추가적인 메모리를 사용합니다.
3. 가비지 컬렉터의 세대 개념에 대해 설명해주세요.
가비지 컬렉터의 세대(Generation) 개념은 메모리를 효율적으로 관리하기 위해 객체의 생존 기간에 따라 메모리를 나누는 방식입니다. 일반적으로 다음과 같은 세대로 구분됩니다:
- Young Generation: 새롭게 생성된 객체들이 할당되는 공간으로, 대부분의 객체는 여기에서 생성되고 곧바로 가비지 컬렉션 됩니다.
- Old Generation (또는 Tenured Generation): Young Generation을 지나 오래 살아남은 객체들이 이동되는 공간으로, 가비지 컬렉션이 상대적으로 덜 빈번하게 발생합니다.
- Permanent Generation (또는 Metaspace): 클래스, 메소드와 같은 메타데이터가 저장되는 공간으로, Java 8 이후에는 Metaspace로 대체되었습니다.
이 세대 구분은 가비지 컬렉션의 효율성을 높이기 위한 것으로, Young Generation에서 주로 많이 발생하는 짧은 생명 주기의 객체들을 빠르게 정리하고, Old Generation에서는 오랫동안 살아남은 객체들을 관리합니다.
4. 박싱, 언박싱을 사용할 때 주의해야 할 점은 무엇일까요?
박싱(Boxing), 언박싱(Unboxing)을 사용할 때 주의해야 할 점은 무엇일까요?
박싱은 기본 데이터 타입을 객체로 변환하는 과정이고, 언박싱은 객체를 기본 데이터 타입으로 변환하는 과정입니다. 주의해야 할 점은 다음과 같습니다:
- 성능: 박싱과 언박싱은 추가적인 연산이 필요하기 때문에 성능 저하를 일으킬 수 있습니다. 특히, 반복문 안에서 빈번하게 발생하면 문제가 됩니다.
- NullPointerException: 언박싱 시 객체가 null인 경우 NullPointerException이 발생할 수 있습니다.
- 불필요한 오버헤드: 불필요한 박싱/언박싱을 피하여 메모리와 CPU 오버헤드를 줄여야 합니다.
5. 오브젝트 풀을 사용하면 메모리 관리에 도움이 되는 이유가 무엇일까요?
오브젝트 풀(Object Pool)은 재사용 가능한 객체들을 미리 생성해 두고 필요할 때마다 재사용하는 디자인 패턴입니다. 다음과 같은 이유로 메모리 관리에 도움이 됩니다:
- 객체 생성 비용 절감: 객체를 미리 생성해 두고 재사용함으로써 빈번한 객체 생성과 소멸로 인한 오버헤드를 줄입니다.
- 메모리 효율성: 객체를 재사용하므로 메모리 할당과 해제 빈도가 줄어들어 메모리 효율성이 높아집니다.
- 성능 향상: 초기화 비용이 큰 객체의 경우 오브젝트 풀을 통해 성능을 크게 향상시킬 수 있습니다.
- 메모리 누수 방지: 필요한 시점에 객체를 반환하고 재사용하기 때문에 메모리 누수를 방지할 수 있습니다.
실습 문제
namespace _06.가비지_컬렉터_실습
{
public class Enemy
{
public int HP { get; set; }
public int Attack { get; set; }
public Enemy(int hp, int attack)
{
HP = hp;
Attack = attack;
}
public void Reset(int hp, int attack)
{
HP = hp;
Attack = attack;
}
}
public class EnemyPool
{
public List<Enemy> availableEnemies = new List<Enemy>();
public List<Enemy> usedEnemies = new List<Enemy>();
public Enemy GetEnemy(int hp, int attack)
{
Enemy enemy;
if (availableEnemies.Count > 0)
{
// TODO : 재사용 가능한 Enemy 객체를 가져오고 초기화
enemy = availableEnemies[availableEnemies.Count - 1];
availableEnemies.RemoveAt(availableEnemies.Count - 1);
enemy.Reset(hp, attack);
}
else
{
enemy = new Enemy(hp, attack);
}
usedEnemies.Add(enemy);
return enemy;
}
public void ReleaseEnemy(Enemy enemy)
{
// TODO : 사용한 Enemy 객체를 반환하고, 재사용 리스트에 추가
//???
usedEnemies.Remove(enemy);
availableEnemies.Add(enemy);
}
}
public class Program
{
public static void Main()
{
EnemyPool enemyPool = new EnemyPool();
List<Enemy> currentEnemies = new List<Enemy>();
long beforeGcCount = GC.CollectionCount(0);
for (int frame = 0; frame < 10000; frame++)
{
// 일정 프레임마다 새로운 적 객체 생성
if (frame % 10 == 0) // 10프레임마다 적 생성
{
Enemy newEnemy = enemyPool.GetEnemy(100, 10);
currentEnemies.Add(newEnemy);
}
// 일정 조건에서 적 객체 반환
if (frame % 20 == 0 && currentEnemies.Count > 0) // 20프레임마다 적 반환
{
Enemy oldEnemy = currentEnemies[0];
currentEnemies.RemoveAt(0);
enemyPool.ReleaseEnemy(oldEnemy);
}
}
long afterGcCount = GC.CollectionCount(0);
Console.WriteLine("Total enemies created: " + (enemyPool.usedEnemies.Count + enemyPool.availableEnemies.Count));
Console.WriteLine("GC collection count before: " + beforeGcCount);
Console.WriteLine("GC collection count after: " + afterGcCount);
Console.WriteLine("GC collections occurred: " + (afterGcCount - beforeGcCount));
}
}
}
.
'내일배움캠프 > 꾸준CS과제' 카테고리의 다른 글
[TIL 24.07.10] 08.선형 자료구조 - LinkedList (0) | 2024.07.10 |
---|---|
[TIL 24.07.09] 07. C# 심화 문법 (0) | 2024.07.09 |
[TIL 24.07.05] 05. 상속과 인터페이스 (0) | 2024.07.05 |
[TIL 24.07.03] 03. 콜백, delegate, event (0) | 2024.07.03 |
[TIL 24.07.02] 02. 객체지향 프로그래밍 (0) | 2024.07.02 |