Elasticsearch를 처음 설치하다 보면 항상 보는 에러들이 몇 가지 있습니다.

 1) root 계정으로 실행할 수 없으며

 2) openfiles, max process 값을 수정해야하고

 3) swappiness 값 설정 변경 요청도 있습니다.

Elasticsearch 실행 시 발생한 Max user processes Error

그 중에 elasticsearch openfiles max process는 최소 65535로 설정하라며, 커널에서 해주는 기본 설정보다 큰 값을 요구하는데요,

오늘은 해당 파라미터가 정확히 어떤 것을 가르키는지 그리고 설정된 값 이상이 되면 어떤 일이 일어나는지 알아보겠습니다.

 

 

1. Max user processes

Max user processes의 의미는 하나의 계정에서 최대로 실행할 수 있는 process의 개수를 말합니다. OS에서의 확인은

# ulimit -a (sofe ulimit)

# ulimit -aH (hard ulimit)

위 명령으로 확인 가능합니다. (현재 제 서버의 설정 값은 아래와 같습니다.)

max user processes

그럼 아래 테스트 코드를 통해 설정된 max user processes의 값을 넘기면 어떻게 되는지 확인해보겠습니다.

4000개 Thread 생성 후 유지

대략적인 코드의 내용은 HTTP 요청이 오면 비동기로 4천개의 Thread를 동시에 생성하고 20분간 유지하는 코드입니다.

그리고 실제로 Thread를 늘려보면,

Thread 생성 중 OOM 발생

그림처럼 ulimit -a 명령의 max user processes 결과와 같이 ec2-user 계정은 1024개까지 스레드를 생성한 후, OOM 에러 메세지를 발생하며 멈춰버리게 됩니다. (이 경우 ec2-user 계정에서는 쉘 명령어 입력 등 어떤 작업도 동작하지 않습니다.)

<리눅스에서는 스레드와 프로세스를 동일하게 봅니다.>

 

 

2. Open files

다음은 Open files입니다. 이 값은 프로세스가 가질 수 있는 최대 파일의 개수를 의미합니다.

리눅스는 모든 프로세스나 세션(소켓)들을 파일로 관리하기 때문에 이 값의 설정은 서버에서의 Connection연결 작업에 매우 중요한 내용이 됩니다.

(설정 값을 넘어가게 되면 리눅스에서 파일을 열 수 없어 프로세스 실행이나 Connection 연결이 안되기 때문!)

그럼 소켓을 open files 값 보다 많이 만들어 테스트를 해보겠습니다.

 

(1) Java 환경 TEST

 - RestTemplate로 소켓 생성 요청을 보내고, 요청을 받은 서버에서 20분간 응답을 대기 시킵니다.

 - 요청을 동시에 1100개를 보내 open files 1024로 제한인 서버에 에러가 발생하면 테스트 성공입니다.

   (기본적으로 java, spring 관련 file이 몇 개 오픈됩니다.)

아래는 테스트 코드 일부입니다.

connection 소켓 생성 후 응답 대기 Code

그리고 동시에 요청을 보낼 API 요청 스크립트를 작성합니다.

API 요청 쉘

쉘을 실행하여 테스트를 진행하면

openfiles 개수가 1024 값을 넘어 1516개 까지 열림!

위 그림처럼 소켓이 1024개가 넘어도 에러 메시지 없이 동작하는 것을 볼 수 있습니다. 그럼 한번 더 스크립트를 실행해보겠습니다.

2048개 까지 생성된 모습

이제 에러가 발생했습니다. 파일은 딱 2048개 까지만 열린 것을 확인할 수 있는데요, openfiles의 값은 soft로 설정된 값이 아닌

그림처럼 Hard 옵션 값까지 file이 생성된다는 것을 알 수 있습니다.

그런데…! 알고 보니 java 환경에서는 hard 값을 따라가는게 맞지만 Python에서는 아니라고 합니다

 

(2) Python 환경 TEST

빨간 박스 : 1024개의 열린 파일 생성 / 파란 박스 : 1024개 이후 파일을 하나 더 열어본다

위 처럼 파이썬 스크립트로 file을 임의로 열어봅니다. 1021개 까지는 파일이 문제없이 열리지만(stdin, stdout, stderr 표준 입/출력 3개 제외)

이후로 여는 파일에서는 Too many open files 에러가 발생합니다!

 

(3) Java Python 환경의 차이?

테스트 결과 Java hard 옵션까지 파일이 오픈되고 Python soft 값까지 오픈됨을 알 수 있었습니다. 그 이유를 찾기 위해 strace 명령을 통해 java 프로세스 동작을 추적해보면

kernel 5.15ver에서 테스트 (이전 버전은 prlimit64 함수 대신 getrlimit, setrlimt 함수 사용)

위 그림처럼 java 프로세스가 시작됨과 동시에 prlimit64라는 함수가 rlim_cur=1024값을 rlim_max=2*1024(hard limit) 값까지 업데이트하는 로그가 찍혀있는 것을 볼 수 있었습니다.

JDK에서 MaxFDLimit 옵션 활성화 (기본값)

그 이유는 설치된 JVM에서 MaxFDLimit 옵션을 통해 limit을 올려주었기 때문이라고 합니다! (기본적으로 활성화된 옵션)

, 리눅스 OS에서는 JDK 실행 시 커널에서 prlimit64 함수를 통해 자동으로 limit 사이즈를 증가시켜준다는 것을 알 수 있었습니다.

 

 

3. 정리

 - (Linux 환경) JAVA에서 동시에 생성 가능한 쓰레드 수는 Max user processes를 따라간다.

 - (Linux 환경) JAVA에서 소켓 통신(API나 모든 Connection) open files 옵션을 따라간다.

  -> JAVA에서는 JDK 코드에서 자동으로 soft limit값은 hard limit값까지 업데이트 됨

  -> Python soft limit 값에서 제한이 걸림

 

나의 개발 환경에 맞게, Max user processes open files의 적절한 값을 찾아 설정하는 것이 중요하겠습니다!

 

 

 

<공동저자>

https://giraffe-lee.tistory.com/7

 

<참조>

https://techblog.woowahan.com/2569/

네트워크 구성하기
VPC, Subnet, Route Table, Internet Gateway, EndPoint 그리고 IaC

 

AWS VPC (Virtual Private Cloud)란?

AWS Cloud 내부에서 구성되는 사용자의 AWS 계정 전용 가상 네트워크로 이곳에서 AWS 리소스를 시작할 수 있다.

AWS에서는 디폴트로 Amazon EC2-VPC를 제공하지만 Amazon VPC는 AWS의 확장 가능한 인프라를 사용한다는 이점과 함께 고객의 자체 데이터 센터에서 운영하는 기존 네트워크와 매우 유사하다.

 

또한 AWS VPC는 AWS 클라우드에서 다른 가상 네트워크와 논리적으로 분리되어 있다. IP 주소 범위와 VPC 범위를 설정하고 서브넷을 추가하고 보안 그룹을 연결한 다음 라우팅 테이블을 구성한다.

VPC는 Amazon 콘솔에서 생성된다. 또한 하나의 VPC는 하나의 Region내에서만 생성이 가능하지만 두개 이상의 리전에 걸치는 것은 불가능하다. 하지만 하나의 VPC는 여러 개의 AZ에 걸쳐서 생성될 수 있고 가질 수 있는 IP 주소의 range는 2^16으로 제한된다.

 

 

VPC는 독립된 하나의 네트워크를 구성하기 위한 가장 큰 단위이다. 각 region에 종속되며 [RFC1918](https://tools.ietf.org/html/rfc1918)이라는 사설 IP 대역에 맞추어 설계해야 한다. VPC에서 사용하는 사설 IP 대역은 아래와 같다.

 

1️⃣ 10.0.0.0 ~ 10.255.255.255(10/8 prefix)

2️⃣ 172.16.0.0 ~ 172.31.255.255(182.16/12 prefix)

3️⃣ 192.168.0.0 ~ 192.168.255.255(192.168/16 prefix)

 

각 대역폭마다 고정되어있는 prefix가 다르다. IPv4 주소인 xxx.xxx.xxx.xxx에서 .으로 구분된 xxx은 0~255 사이의 숫자이며 이는 8bits로 표현할 수 있다. 즉 1번은 00001010~, 2번은 0101100.0001~, 3번은 11000000.10101000~가 앞에 고정되어 있고 나머지 주소 범위에 해당하는 IP 주소를 할당할 수 있다. 아래와 같이 10.0.0.0/16으로 설정된 IP 대역폭은 총 65,536개의 프라이빗 IPv4 주소를 가진다.

 

VPC에서 한번 설정된 IP 대역은 수정할 수 없으며 각각의 VPC는 독립적이기 때문에 서로 통신할 수 없다. 만일 통신을 원한다면 VPC 피어링 서비스를 통해 VPC 간에 트래픽을 라우팅할 수 있도록 설정할 수 있다.

 

 

 

public / private Subnet이란?

서브넷은 VPC의 IP 주소를 나누어 리소스가 배치되는 물리적인 주소 범위를 뜻한다. VPC를 잘게 나눈 것이 서브넷이기 때문에 VPC보다 대역폭이 낮으며 하나의 AZ(Availability Zone)에 하나의 서브넷이 연결되기 때문에 region의 AZ 수를 미리 확인하고 설정해야한다.

 

서브넷은 다시 Public SubnetPrivate Subnet으로 나뉠 수 있다. 인터넷과 연결되어있는 서브넷을 public subnet이라고 하고 인터넷과 연결되어있지 않은 서브넷을 private subnet이라고 한다. 이처럼 인터넷 연결 여부로 subnet을 구분하는 이유는 보안을 강화하기 위함이다. public subnet에 존재하는 인스턴스는 인터넷에 연결되어 아웃바운드, 인바운드 트래픽을 주고받을 수 있다. 반면 private subnet은 외부에 노출이 되어 있지 않기 때문에 접근할 수 없다. 즉 인터넷과 연결되어 외부에 노출되어 있는 면적을 최소화함으로써 네트워크 망에 함부로 접근하는 것을 막기 위함이다.

 

VPC 내에는 보통 public subnet과 private subnet으로 구성되어 있다.

  • public subnet
    • internet gateway, ELB, public IP/Elastic IP를 가진 인스턴스를 내부에 가지고 있다.
    • 특히 Public subnet 내에 있는 nat instance를 통해서 private subnet 내에 있는 instances이 인터넷이 가능하게 한다.
  • private subnet
    • 기본적으로 외부와 차단되어 있다.
    • private subnet 내의 인스턴스들은 private ip만을 가지고 있고 internet inbound/outbound가 불가능 하고 오직 다른 서브넷과의 연결만이 가능하다.

 

 

Router

라우터는 VPC 안에서 발생한 네트워크 요청을 처리하기 위해 어디로 트래픽을 전송해야 하는지 알려주는 표지판 역할을 수행한다. 각각의 서브넷은 네트워크 트래픽 전달 규칙에 해당하는 라우팅 테이블을 가지고 있으며 요청이 발생하면 가장 먼저 라우터로 트래픽을 전송한다. 일반적으로 VPC 내부 네트워크에 해당하는 주소는 local로 향하도록 한다

 

 

 

Routing Table

네트워크상의 특정 목적지까지의 거리와 가는 방법등을 명시하고 있는 테이블로 라우터는 어떤 목저지를 찾아갈 때 이 라우팅 테이블을 보고 찾아가게 된다. 쉽게 말하면 라우터가 여러 가지 정보를 종합해서 얻어낸 네트워크에 대한 지도를 라우팅 테이블이라고 한다.

 

ip address에 routing 경로를 정의하여 subnet에서 밖으로 나가는 outbound traffic에 대한 routing 경로를 설정하는 것

VPC 내에는 subnet이 있고 각 subnet은 각기 다른 네트워크 대역을 가지고 있고 한 subnet에서 다른 한쪽 subnet으로 가기 위해서는 라우팅이 필요하다.

VPC 내부에 대해서는 자동으로 라우팅이 생성되기 때문에 별다른 설정 없이 한 subnet에서 다른 subnet으로 통신이 가능하다. 이는 보이지 않는 암시적 라우터인 vpc router를 통해 가능하게 되는 것이다. subnet으로 리소스를 보낼 때 이 vpc router를 거쳐야 한다.

 

AWS에서의 라우팅 테이블은 subnet이 라우터를 통해 보내는 네트워크 트래픽이 전달되는 위치를 제어하는 역할을 한다. 귐게 말해 라우팅에 대한 하나의 규칙을 정의하는 것이다.

 

 

 

 

Internet Gateway

VPC와 인터넷이 통신할 수 있도록 도와주는 VPC 컴포넌트

단순히 인터넷과의 통로 역할을 할 뿐 아니라 NAT의 역할도 수행한다.

인터넷 게이트웨이는 VPC 리소스와 인터넷 간 통신을 활성화하기 위해 VPC에 연결하는 게이트웨이이다. 앞서 설명했듯 public subnet만 외부와 통신해야 하므로 public subnet의 라우팅 테이블에만 인터넷 게이트웨이로 향하는 규칙을 포함한다. 아래 그림과 같이 라우팅 규칙을 설정하면 네트워크 요청 발생 시 목적지의 IP 주소가 10.0.0.0/16(VPC 내부)에 해당하는지 확인한다. 해당하지 않는 모든 트래픽은 인터넷 게이트웨이를 통해 외부로 전송되어 목적지를 찾는다.

 

 

 

NAT Gateway

public subnet만 인터넷 게이트웨이를 통해 외부와 트래픽을 주고받을 수 있다면 private subnet의 트래픽은 무조건 VPC 안에서만 처리된다는 뜻일까?

 

그렇지 않다. private subent 역시 마찬가지로 인터넷과 통신할 수 있다. 하지만 private subnet에서 직접하는 것은 불가능하므로 트래픽을 public subnet에 속한 인스턴스에 전송해서 인터넷과 통신을 해야 한다.

 

NAT 게이트웨이가 이 역할을 수행한다. private subent에서 발생하는 네트워크 요청이 VPC 내부의 주소를 목적지로 하는 것이 아니라면 public subnet에 존재하는 NAT로 트래픽을 전송한다. 트래픽을 받은 NAT는 public subnet의 라우팅 규칙에 따라 처리함으로써 private subnet이 인터넷과 통신할 수 있도록 한다.

 

 

 

 

VPC EndPoint (private Link, VPC 엔드 포인트)

VPC 엔드포인트는 인터넷 게이트웨이나 NAT 게이트웨이와 같은 다른 게이트웨이 없이 AWS 서비스와 연결하여 통신할 수 있는 private connection을 제공하는 서비스이다. Private link를 통해 AWS 서비스와 연결함으로써 데이터를 인터넷에 노출하지 않고 바로 접근할 수 있다.

또한 연결하는 서비스의 IP 주소를 바꾸는 등 네트워크 정보의 변경 등의 작업에서 불필요하게 발생하는 비용을 줄일 수 있다.

 

 

 

 

코드형 인프라 IaC

수동 프로세스가 아닌 코드를 통해 인프라를 관리하고 프로비저닝하는 것을 말함

IaC를 사용하면 인프라 사양을 담은 구성 파일이 생성되므로 구성을 편집하고 배포하기 더 쉬워진다. 또한 IaC는 매번 동일한 환경을 프로비저닝하도록 보장한다.

 

IaC는 구성 사양을 코드화하고 문서화 함으로써 구성 관리를 지원하며, 따라서 구성 변경 사항을 문서화하지 않고 임시로 변경하는 일을 막을 수 있다.

버전 제어는 IaC의 가장 중요한 부분이고다른 소프트웨어 소스 코드 파일과 마찬가지로 구성 파일도 소스 제어가 필요하다.

 

코드로 인프라를 배포한다는 것은 인프라를 모듈식 구성 요소로 분할하고 자동화를 통해 다양한 방식으로 결합할 수 있다는 뜻이기도 하다.

IaC로 인프라 프로비저닝을 자동화하면 애플리케이션을 개발하거나 배포할 때 마다 개발자가 직접 서버, 운영 체제, 스토리지, 기타 인프라 구성 요소를 수동으로 프로비저닝하고 관리할 필요가 없어진다.

 

인프라를 코드화하여 템플릿을 만들고 프로비저닝할 때 이 템플릿을 사용하면 된다. 이러한 작업은 수동으로 진행할 수 도 있고 자동화 툴을 사용할 수 도 있다.

 

 


reference.

link

'🔥 > Cloud' 카테고리의 다른 글

[Cloud] 클라우드 컴퓨팅의 유형  (2) 2022.01.13

기본적으로 프라이빗 클라우드, 퍼블릭 클라우드, 하이브리드 클라우드, 멀티클라우드로 4가지의 유형으로 나눌 수 있다.

 

퍼블릭 클라우드

일반적으로 최종 사용자가 소유하지 않은 IT 인프라에서 생성되는 클라우드 환경

전통적으로 오프프레미스에서 구동되었지만, 오늘날 퍼블릭 클라우드 제공업체는 클라이언트의 온프레미스 데이터 센터에서 클라우드 서비스를 구동하기 시작했다. 이로 인해 위치와 소유권을 통한 구분은 사라지게 되었다.

환경이 멀티플 테넌트로 파티셔닝 또는 재배포되는 클라우드를 모두 퍼블릭 클라우드라고 볼 수 있다.

멀티 테넌시는 단일 소프트웨어 인스턴스로 서로 다른 여러 사용자 그룹에 서비스를 제공할 수 있는 소프트웨어 아키텍처로 SaaS 제품이 그 예이다.

서로 다른 고객이 서버 리소스를 나누어 사용하는 공유 호스팅을 멀티 테넌시라고 부르기도 한다.

반대의 의미인 단일 테넌시에서는 소프트웨어 인스턴스 또는 컴퓨터 시스템 하나에 최종 사용자 또는 사용자 그룹이 하나만 있다.

  • 대표적인 퍼블릭 클라우드 제공업체
    • AWS
    • Google Cloud
    • Microsoft Azure
    • etc

 

 

 

프라이빗 클라우드

단일 최종 사용자 또는 그룹의 전용 클라우드 환경으로, 실행 시 대개 해당 사용자 또는 그룹의 방화벽으로 보호된다.

완전히 독립적인 액세스 권한이 있는 단일 고객만 기반 IT 인프라를 독점적으로 사용하는 경우 이러한 모든 클라우드를 프라이빗 클라우드라고 정의할 수 있다.

그러나 이제는 프라이빗 클라우드라고 해서 온프레미스 IT 인프라에서만 구동할 수 있는 것은 아니다. 또 오프 프레미스에 위치한 벤더 소유의 임대한 데이터 센터에 프라이빗 클라우드를 구축하는 조직도 있기 때문에 위치와 소유권은 더이상 차별화 요소가 아니다.

이로 인해 프라이빗 클라우드의 하위 유형도 여러 가지가 생겨났다.

  • 관리형 프라이빗 클라우드

고객은 타사 공급 업체가 배포, 설정 및 관리하는 프라이빗 클라우드를 구축하고 사용

관리형 프라이빗 클라우드는 IT 팀의 인력이나 기술이 부족한 경우 보다 나은 프라이빗 클라우드 서비스와 인프라를 제공할 수 있도록 기업을 지원하는 클라우드 제공옵션이다.

 

  • 전용 클라우드

다른 클라우드 내에 있는 클라우드

퍼블릭 클라우드 또는 프라이빗 클라우드에 전용 클라우드를 구축할 수 있다.

예를 들어, 회계 부서에서는 조직의 프라이빗 클라우드 내에 전용 클라우드를 보유할 수 있다.

 

 

 

 


하이브리드 클라우드

단일 IT 환경 처럼 보이지만, 실제로는 여러 환경이 LAN, WAN, VPN 및/또는 API를 통해 연결된 형태다.

하이브리드 클라우드는 복잡하고 어떤 상황인지에 따라 요건 또한 다르다.

예를 들어 하이브리드 클라우드에는 다음이 포함될 수 있다.

  • 1개 이상의 프라이빗 클라우드와 1개 이상의 퍼블릭 클라우드
  • 2개 이상의 프라이빗 클라우드
  • 2개 이상의 퍼블릭 클라우드
  • 1개 이상의 퍼블릭 클라우드 또는 프라이빗 클라우드에 연결되는 베어메탈 또는 가상 환경

애플리케이션을 개별적이지만 연결되어있는 다중 환경 내외로 이동할 수 있는 경우 하이브리드 클라우드라고 볼 수 있으며, 사실상 거의 모든 IT 시스템이 이러한 추세다.

적어도 이러한 환경 중 일부는 온디맨드로 확장할 수 있는 통합 IT 리소스에서 제공되어야 하고, 모든 환경이 통합 관리 및 오케스트레이션 플랫폼을 사용해 단일 환경처럼 관리되어야 한다.

 

 

 

 

 

멀티클라우드

2곳 이상의 클라우드 공급업체가 제공하는 2개 이상의 퍼블릭 또는 프라이빗 클라우드로 구성된 클라우드 접근 방식

모든 하이브리드 클라우드는 멀티 클라우드이지만, 모든 멀티클라우드가 하이브리드 클라우드인 것은 아니다.

멀티 클라우드가 어떤 형태로든 통합 또는 오케스트레이션되어 연결되면 하이브리드 클라우드가 된다.

 

명확한 목적, 즉 중요 데이터를 더 효과적으로 제어하기 위해 또는 더 강력한 재해 복구를 위한 이중 스토리지 공간으로서 멀티클라우드 환경을 구축하거나 우연히 멀티 클라우드의 형태가 될 수 도 있다. 어느 쪽이든 확장된 환경 포트폴리오를 통해 보안과 성능을 강화하려는 기업들 사이에서 멀티 클라우드가 보편화되고 있다.

'🔥 > Cloud' 카테고리의 다른 글

[Cloud] VPC, Subnet, Route etc...  (1) 2022.01.13
자바는 OS에 독립적인 특징을 가지고 있다.
JVM이 OS와 프로그램의 사이에서 기계어로 행석앻주는 역할을 하기 때문이다.

 

JVM JAVA Virtual Machine

컴파일된 바이트 코드를 기계가 이해할 수 있는 기계어로 변환

스택기반의 가상 머신 

메모리 관리와 GC를 수행

 

자바 코드의 실행 과정

1. 개발자가 자바코드를 작성한다. 

2. ( .java ) 인 파일을 자바 컴파일러를 통해 자바 바이트 코드로 컴파일한다.

3. 컴파일 된 바이트 코드를 JVM의 Class Loader에 전달한다. 

4. Class Loader는 동적 로딩을 통해 필요 클래스들을 로딩 및 링크하여 Runtime Data Area에 올린다. (JVM의 메모리)

5. 실행 엔진은 JVM메모리에 올라온 바이트 코드들을 명령어 단위로 하나씩 가져와서 실행한다.

 

 

 

 

 

JVM
Class Loader + Runtime Data Areas + Execution Engine

 

 

 

 

Class Loader 특징

1. 계층 구조 

클래스 로더는 여러 클래스 로더끼리 부모-자식 관계를 이루어 계층적인 구조로 되어 있다.

 

  • 부트스트랩 클래스 로더 Bootstrap Class Loader
    • 최상위 클래스로더, 유일하게 JAVA가 아닌 네이티브 코드로 구현
    • JVM이 실행될 때 같이 메모리에 올라감
    • Object 클래스를 비롯하여 JAVA API들을 로드함

 

  • 익스텐션 클래스 로더 Extension Class Loader
    • 기본 JAVA API를 제외한 확장 클래스들을 로드함 - 다양한 보안 확장 기능 로드

 

  • 시스템 클래스 로더 System Class Loader
    • (부트 스트랩과 익스텐션 클래스로더가 JVM 자체의 구성 요소들을 로드한다) 시스템 클래스 로더는 어플리케이션의 클래스들을 로드함
    • 사용자가 지정한 $CLASSPATH 내의 클래스들을 로드함

 

  • 사용자 정의 클래스 로더 User-Defined Class Loader
    • 어플리케이션 사용자가 직접 코드 상에서 생성하여 사용하는 클래스 로더
    • WAS와 같은 프레임워크는 서로 독립적으로 동작하게 하기 위해서 이를 위해 사용자 정의 클래스 로더들을 사용하여 클래스 로더의 위임 모델을 통해 어플리케이션의 독립성을 보장함

 

2. 위임모델 

처음 바이트 코드를 넘겨받은 클래스로더.

필요한 클래스를 로드할 때 혹은 실행 엔진에서 명령어 단위로 바이트 코드를 실행하다가 처음으로 참조하는 클래스에 대해 클래스 로더에게 로드를 요청할 때.

로드를 요청받은 클래스 로더는 다음 순서대로 요청받은 클래스가 있는지 확인함

  1. 클래스 로더 캐시
  2. 상위 클래스 로더
  3. 자기 자신

이전에 로드된 클래스인지 클래스 로더 캐시를 확인한다.

없다면 상위 클래스 로더를 하나씩 거슬러 올라가며 확인하는데, 이 때 올라가는 도중에 클래스를 발견하더라도 부트 스트랩 클래스 로더(최상단 로더)까지 확인을 해서 부트 스트랩 클래스 로더에도 해당 클래스가 존재한다면 그 클래스를 로드하게 된다.

 

 

3. 가시성 제한

클래스 로더가 클래스 로드를 요청받았을 때 위임 모델에 의해서 클래스 로더 캐시를 확인하고 없으면 상위 클래스 로더를 확인한다. 이때 하위 클래스는 확인이 불가능한 특성이 바로 가시성 제한이다.

 

 

4. 언로드 불가

클래스를 로드하는 것은 가능하지만 그 반대로 unload하는 것은 불가능하다.

 

5. 이름 공간

네임 스페이스는 각 클래스 로더들이 가지고 있는 공간으로 로드된 클래스를 보관하는 공간

위임 모델을 통해서 상위 클래스 로더들을 확인해야할 때 확인하는 공간이 바로 이름 공간

네임 스페이스에 보관되는 기준은 FQCN Fully Qualified Class Name 으로 패키지 명까지 포함되어있는 식별자를 뜻한다.

 

각각의 클래스 로더가 각자 네임 스페이스를 가지고 있기 때문에 FQCN이 같은 클래스라도 네임스페이스가 다르면 다른 클래스로 갖주한다. ( 이 기능을 통해 로드한 클래스 로더를 제거하고 언로드의 효과를 줄 수 있다. )

 

 

클래스 로드 과정 

 

1. 로드 - 클래스 파일을 가져와서 JVM의 메모리에 로드한다.

2. 검증 - 클래스 로드 전 과정 중에서 가장 복잡하고 시간이 많이 걸리는 과정으로, 읽어들인 클래스가 자바 언어 명세 및 JVM 명세에 명시된 대로 구성되어 있는지 검사한다.

3. 준비 - 클래스가 필요로 하는 메모리를 할당한다. 

4. 분석 - 클래스의 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경한다.

5. 초기화 - 클래스 변수들을 적절한 값으로 초기화한다. (static 필드들은 설정된 값으로 초기화 )

 

 

 

 

 

 

런타임 데이터 영역 Runtime Data Area

JVM이 OS 위에서 실행되면서 할당받는 메모리 영역이 바로 런타임 데이터 영역이다.

이 영역은 크게 5가지, 세분화시 6가지로 나눠 볼 수 있다. 

PC 레지스터, JVM 스택, 네이티브 메서드 스택은 스레드마다 하나씩 생성되고 

힙, 메서드 영역은 모든 스레드가 공유해서 사용된다.

 

 

  • PC 레지스터 
    • Program Counter 레지스터는 현재 수행 중인 명령의 주소를 가지며 스레드가 시작될 때 생성되며, 각 스레드마다 하나씩 존재함
  • JVM 스택 
    • 스택 프레임이라는 구조체를 저장하는 스택
    • 예외 발생 시 printStackTrace( ) 메서드로 보여주는 Stack Trace의 각 라인 하나가 스택 프레임을 표현함
    • 스레드가 시작될 때 생성되며 각 스레드마다 하나씩 존재함
  • 네이티브 메서드 스택
    • 자바 외의 언어로 작성된 네이티브 코드를 위한 스택
    • JAVA Native Interface를 통해 호출하는 C/C++ 등의 코드를 수행하기 위한 스택으로, 언어에 맞게 스택 생성
    • 인스턴스 또는 객체를 저장하는 공간
    • Garbage Collection의 대상
    • JVM 성능 등의 이슈에서 가장 많이 언급되는 공간
    • 힙 구성 방식이나 가비지 컬렉션 방법 등은 JVM 벤더들의 재량
  • 메서드 영역 
    • 모든 스레드가 공유하는 영역
    • JVM이 시작될 때 생성된다.
    • JVM이 읽어 들인 각각의 클래스와 인터페이스에 대한 런타임 상수 풀, 필드와 메서드에 대한 정보, static 변수, 메서드의 바이트 코드들을 보관함
  • 런타임 상수 풀
    • JVM 동작에서 가장 핵심적인 역할을 수행하는 곳
    • 각 클래스와 인터페이스의 상수 뿐만 아니라, 메서드와 필드에 대한 모든 레퍼런스까지 담고 있는 테이블
    • 어떤 메서드나 필드를 참조할 때 JVM은 런타임 상수 풀을 통해 해당 메서드나 필드의 실제 메모리상 주소를 찾아서 참조한다.

 

 

 

 

 

실행 엔진 Execution Engine

실행 엔진은 클래스 로더를 통해 런타임 데이터 영역에 배치된 바이트 코드를 명령어 단위로 읽어서 실행

 

바이트 코드와 각 명령어는 1바이트 크기의 OpCode와 추가 피연산자로 이뤄져있음

실행 엔진은 하나의 OpCode를 가져와서 피연산자와 작업을 수행한 다음 OpCode를 수행하는 식으로 동작한다.

 

이 수행 과정에서 실행 엔진은 바이트 코드를 기계가 실행할 수 있는 형태로 변경한는데 두가지 방법을 사용한다.

  • 인터프리터 
    • 바이트 코드 명령어를 하나씩 읽어서 해석하고 실행한다. 하나하나의 해석은 빠르지만 전체적인 실행 속도는 느리다는 단점을 가진다. 
    • JVM 안에서 바이트 코드는 기본적으로 인터프리터 방식으로 동작한다.
  • JIT 컴파일러 Just-In-Time
    • 인터프리터의 단점을 보완하기 위해 도입된 방식
    • 바이트 코드 전체를 컴파일하여 네이티브 코드로 변경하고, 이후에는 해당 메서드를 더이상 인터프리팅 하지 않고 네이티브 코드로 직접 실행하는 방식
    • 전체적인 실행 속도는 인터프리팅방식보다 빠르다 ( 네이티브 코드는 캐시에 보관하기 때문에 한 번 컴파일된 코드는 캐시에서 바로 꺼내어 실행해 빠르게 수행된다 )
    • 하지만 JIT 컴파일러가 컴파일하는 과정은 바이트 코드를 하나씩 인터프리팅 하는 것보다 훨씬 오래 걸리기 때문에 JVM은 해당 메서드가 얼마나 자주 호출되고 실행되는 지를 체크하고, 일정 기준을 넘었을 때만 JIT 컴파일러를 통해 컴파일 하여 네이티브 코드를 생성한다.

 

 

 


참조

'🔥 > JAVA' 카테고리의 다른 글

[JAVA] 객체 지향 프로그래밍(OOP)이란?  (0) 2021.08.11
[JAVA] 자바가상머신, JVM이란?  (0) 2021.08.11
[JAVA] HashMap과 HashTable의 차이점  (0) 2021.08.04
[JAVA] Hash란?  (0) 2021.08.04

11.1 값이 없는 상황을 어떻게 처리할까?

public class Person {
	private Car car;
    public Car getCar(){ return car;}
}

public class Car {
	private Insurance insurance;
    public Insurance getInsurance(){ return insurance;}
}

public class Insurance{
	private String name;
    public String getName(){ return name;}
}

위와 같이 자동차와 자동차 보험을 갖고 있는 사람 객체를 중첩 구조로 구현했다고 하자.

 

public String getCarInsurance(Person person){
	return person.getCar().getInsurance().getName();
}
  • 차를 소유하지 않은 사람이 있지만 getCar을 호출하면
  • person이 null이라면
  • getInsurance가 null을 반환한다면

 

11.1.1 보수적인 자세로 NullPointerException 줄이기

NullPointerException을 피하기 위해서..

null 확인 코드를 추가

if (person != null) {
	Car car = person.getCar();
    if (car != null) {
    	...

반복 패턴 코드를 깊은 의심이라고 부른다. 코드의 구조가 엉망이되고 가독성도 떨어진다.

 

 

if (person == null) {
	return "Unknown"
}
Car car = person.getCar();
if (car == null){
	...

위 처럼 조금 다른 방식으로 if 블록을 없애고 null 변수가 있으면 즉시 "Unknown"을 반환한다.

하지만 출구가 많아져 유지보수에 어려움이 있다.

 

11.1.2 null 때문에 발생하는 문제

  • 에러의 근원이다.
  • 코드를 어지럽힌다.
  • 아무 의미가 없다.
  • 자바의 철학에 위배된다 - 모든 포인터를 숨겼지만 null포인터는 예외다.
  • 형식 시스템에 구멍을 만든다. - null이 어떤 의미로 사용되었는지 알 수 없다.

 

 

11.2 Optional 클래스 소개

java.util.Optional<T> 라는 새로운 클래스 제공한다.

Optional은 선택형 값을 캡슐화하는 클래스이다.

 

값이 있으면 Optional 클래스는 값을 감싼다. 

반면 값이 없으면 Optional.empty 메서드로 Optional을 반환한다.

Optional.empty는 Optional의 특별한 싱글턴 인스턴스를 반환하는 정적 팩토리 메서드다.

 

public class Person {
	// 사람이 차를 소유햇을 수도, 소유하지 않았을 수도 있으므로 Optional정의
	private Optional<Car> car;
    public Optional<Car> getCar(){ return car;}
}

public class Car {
	// 자동차가 보험에 가입되어 있을 수도 아닐 수 도 있으므로
	private Optional<Insurance> insurance;
    public Optional<Insurance> getInsurance(){ return insurance;}
}

public class Insurance{
	// 보험회사에는 반드시 이름이 있다.
	private String name;
    public String getName(){ return name;}
}

 

 

11.3 Optional 적용 패턴

 

11.3.1 Optional 객체 만들기

 

  • 빈 Optional

Optional<Car> optCar = Optional.empty();

 

  • null이 아닌 값으로 Optional 만들기

Optional.of로 null이 아닌 값을 포함하는 Optional을 만들 수 있다.

Optional<Car> optCar = Optional.of(car);

car가 null이라면 즉시 NullPointerException 발생

 

  • null값으로 Optional 만들기

Optional<Car> optCar = Optional.ofNullable(car);

car이 null이면 빈 Optional 객체가 반환된다.

 

 

11.3.2 맵으로 Optional의 값을 추출하고 반환하기

이름 정보에 접근하기 전에 insurance가 null인지 확인해야 한다.

Optional은 map 메서드를 지원한다.

 

Optional<Insurance> optInsurance = Optional,.ofNullable(insurance);
Optional<String> name = optInsurance.map(Insurance::getName);

 

문제보러가기

🕵️‍♀️ 문제


해커 김지민은 잘 알려진 어느 회사를 해킹하려고 한다. 이 회사는 N개의 컴퓨터로 이루어져 있다. 김지민은 귀찮기 때문에, 한 번의 해킹으로 여러 개의 컴퓨터를 해킹 할 수 있는 컴퓨터를 해킹하려고 한다.

이 회사의 컴퓨터는 신뢰하는 관계와, 신뢰하지 않는 관계로 이루어져 있는데, A가 B를 신뢰하는 경우에는 B를 해킹하면, A도 해킹할 수 있다는 소리다.

이 회사의 컴퓨터의 신뢰하는 관계가 주어졌을 때, 한 번에 가장 많은 컴퓨터를 해킹할 수 있는 컴퓨터의 번호를 출력하는 프로그램을 작성하시오.

 

 

 

💡 입력


첫째 줄에, N과 M이 들어온다. N은 10,000보다 작거나 같은 자연수, M은 100,000보다 작거나 같은 자연수이다. 둘째 줄부터 M개의 줄에 신뢰하는 관계가 A B와 같은 형식으로 들어오며, "A가 B를 신뢰한다"를 의미한다. 컴퓨터는 1번부터 N번까지 번호가 하나씩 매겨져 있다.

 

 

 

 

 

📌 출력


첫째 줄에, 김지민이 한 번에 가장 많은 컴퓨터를 해킹할 수 있는 컴퓨터의 번호를 오름차순으로 출력한다.

 

 

 

 

🙋‍♀️ 풀어보자~


A가 B를 신뢰하는 형식이므로 입력을 x, y로 받고 arr[y].append(x)로 해주자.

bfs를 돌릴 때 arr[y]에 연결된 애들이 vistited 된적이 있는지 확인해보고 없으면 방문을 찍고 덱에 첨가해주는 형식으로 작성

 

from collections import deque

n, m = map(int, input().split())

arr = [[] for _ in range(n+1)]

for _ in range(m):
    x, y = map(int, input().split())
    arr[y].append(x)

def bfs(v):
    q = deque([v])
    visited = [False] * (n + 1)
    visited[v] = True
    count = 1
    while q:
        v = q.popleft()
        for e in arr[v]:
            if not visited[e]:
                q.append(e)
                visited[e] = True
                count += 1
    return count

result = []
max_value = -1

for i in range(1, n+1):
    c = bfs(i)
    if c > max_value:
        result=[i]
        max_value=c
    elif c == max_value:
        result.append(i)
        max_value=c

for e in result:
    print(e, end=" ")

 

 

시간 초과 머선일이구,,,

'🕵️‍♀️ > BOJ' 카테고리의 다른 글

[Python] BOJ 1012 유기농 배추 (DFS/BFS)  (0) 2021.11.18
[Python] BOJ 2606 바이러스 (BFS/DFS)  (0) 2021.11.17
코테대비 백준문제추천  (0) 2021.11.05
[Python] BOJ 14719 빗물  (0) 2021.11.04
[Python] BOJ 1149 RGB거리 (dp)  (0) 2021.11.04

문제보러가기

🕵️‍♀️ 문제


차세대 영농인 한나는 강원도 고랭지에서 유기농 배추를 재배하기로 하였다. 농약을 쓰지 않고 배추를 재배하려면 배추를 해충으로부터 보호하는 것이 중요하기 때문에, 한나는 해충 방지에 효과적인 배추흰지렁이를 구입하기로 결심한다. 이 지렁이는 배추근처에 서식하며 해충을 잡아 먹음으로써 배추를 보호한다. 특히, 어떤 배추에 배추흰지렁이가 한 마리라도 살고 있으면 이 지렁이는 인접한 다른 배추로 이동할 수 있어, 그 배추들 역시 해충으로부터 보호받을 수 있다. 한 배추의 상하좌우 네 방향에 다른 배추가 위치한 경우에 서로 인접해있는 것이다.

한나가 배추를 재배하는 땅은 고르지 못해서 배추를 군데군데 심어 놓았다. 배추들이 모여있는 곳에는 배추흰지렁이가 한 마리만 있으면 되므로 서로 인접해있는 배추들이 몇 군데에 퍼져있는지 조사하면 총 몇 마리의 지렁이가 필요한지 알 수 있다. 예를 들어 배추밭이 아래와 같이 구성되어 있으면 최소 5마리의 배추흰지렁이가 필요하다. 0은 배추가 심어져 있지 않은 땅이고, 1은 배추가 심어져 있는 땅을 나타낸다.

 

 

💡 입력


입력의 첫 줄에는 테스트 케이스의 개수 T가 주어진다. 그 다음 줄부터 각각의 테스트 케이스에 대해 첫째 줄에는 배추를 심은 배추밭의 가로길이 M(1 ≤ M ≤ 50)과 세로길이 N(1 ≤ N ≤ 50), 그리고 배추가 심어져 있는 위치의 개수 K(1 ≤ K ≤ 2500)이 주어진다. 그 다음 K줄에는 배추의 위치 X(0 ≤ X ≤ M-1), Y(0 ≤ Y ≤ N-1)가 주어진다. 두 배추의 위치가 같은 경우는 없다.

 

 

 

📌 출력


각 테스트 케이스에 대해 필요한 최소의 배추흰지렁이 마리 수를 출력한다.

 

 

 

 

🙋‍♀️ 풀어보자~


우선 배추밭을 0으로 다 채워넣어야 한다.

그리고 그와 같은 크기로 false 인지 true인지를 담아줄 배열을 만든다. ( 왼쪽 상단이 (0, 0)이 되게 )

array=[[0]*m for _ in range(n)]
visited = [[False]*m for _ in range(n)]

 

그리고 k번씩 돌면서 x, y 에 해당하는 값을 받아준다.

for _ in range(k):
    y, x = map(int,input().split())
    array[x][y] = 1

 

가로로 m까지 세로로 n까지 for문을 돌면서 array와 visited를 통해 배추는 심어져있지만 방문하지 않은 곳을 찾아 방문해주는 dfs나 bfs를 실행시키면된다.

for i in range(n):
    for j in range(m):
        if array[i][j] and not visited[i][j]:
            dfs(i,j) 나 bfs(i,j)

 

 

1. DFS로 풀 경우

재귀 용법을 사용할 것이다. 

 

파이썬의 재귀 깊이 제한은 1000으로 매우 얕기 때문에, 이 문제를 그냥 푼다면 런타임 에러가 난다.

파이썬으로 재귀를 사용해 풀어야하는 문제라면 setrecursionlimit를 쓰는 것은 선택이 아닌 필수다.

 

  • 상하좌우로 움직일 수 있는 방향을 정해주고 이를 for문으로 돌린다.
  • 각 위치의 값이 0보다 크고 최대 길이보다 작은지 1차적으로 확인을 해주고, 이에 해당되지 않는다면 continue를 사용해 다음 위치로 넘겨주게 for문으로 던진다.
  • 맞다면 이젠 확인을 해준다. 이곳에 배추가 있고 방문한 적 이 없는가?

 

# 테스트 케이스 개수를 입력받는다.
# 만약 재귀를 사용해서 풀어야하는 문제라면 setrecursionlimit을 쓰는 것은 선택이 아닌 필수다.
# 파이썬의 기본 재귀 깊이 제한은 1000으로 매우 얕다.
# 따라서 재귀로 문제를 풀경우 이제한에 걸려 에러메세지를 볼수 없다.
# 런타임 에러가 난다.
import sys
sys.setrecursionlimit(100000)

def dfs(x,y):
    visited[x][y] = True
    directions = [ (-1, 0), (1, 0), (0, -1), (0, 1)]
    # 상하 좌우로 움직일 수 있는 방향을 지정해주고 여기서 for문을 돌린다.
    for dx, dy in directions:
        nx, ny = dx + x, dy+y
        if nx < 0 or nx >=n or ny<0 or ny>=m:
            continue
        if array[nx][ny] and not visited[nx][ny]:
            dfs(nx,ny)

for _ in range(int(input())):
    m, n, k = map(int,input().split())
    array=[[0]*m for _ in range(n)]
    visited = [[False]*m for _ in range(n)]
    for _ in range(k):
        y, x = map(int,input().split())
        array[x][y] = 1
    result = 0
    for i in range(n):
        for j in range(m):
            if array[i][j] and not visited[i][j]:
                dfs(i,j)
                result += 1
    print(result)

 

 

 

 

2. BFS로 풀어보면,,,

BFS는 덱..deque 

 

dfs와 마찬가지로 움직일 수 있는 상하좌우의 위치를 담아 for문을 돌려줄 것이다. 

from collections import deque

def bfs(x,y):
    q = deque()
    q.append((x,y))
    visited[x][y] = True
    directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
    while q:
        x, y = q.popleft()
        for dx, dy in directions:
            nx, ny = x+dx, y+dy
            if nx < 0 or nx >= n or ny < 0 or ny >= m:
                continue
            if array[nx][ny] and not visited[nx][ny]:
                q.append((nx,ny))
                visited[nx][ny]=True


for _ in range(int(input())):
    m, n, k = map(int,input().split())
    array=[[0]*m for _ in range(n)]
    visited = [[False]*m for _ in range(n)]
    for _ in range(k):
        y, x = map(int,input().split())
        array[x][y] = 1
    result = 0
    for i in range(n):
        for j in range(m):
            if array[i][j] and not visited[i][j]:
                bfs(i,j)
                result += 1
    print(result)

 

'🕵️‍♀️ > BOJ' 카테고리의 다른 글

[Python] BOJ 1325 효율적인 해킹 (BFS)  (1) 2021.11.19
[Python] BOJ 2606 바이러스 (BFS/DFS)  (0) 2021.11.17
코테대비 백준문제추천  (0) 2021.11.05
[Python] BOJ 14719 빗물  (0) 2021.11.04
[Python] BOJ 1149 RGB거리 (dp)  (0) 2021.11.04

문제보러가기

🕵️‍♀️ 문제


신종 바이러스인 웜 바이러스는 네트워크를 통해 전파된다. 한 컴퓨터가 웜 바이러스에 걸리면 그 컴퓨터와 네트워크 상에서 연결되어 있는 모든 컴퓨터는 웜 바이러스에 걸리게 된다.

예를 들어 7대의 컴퓨터가 <그림 1>과 같이 네트워크 상에서 연결되어 있다고 하자. 1번 컴퓨터가 웜 바이러스에 걸리면 웜 바이러스는 2번과 5번 컴퓨터를 거쳐 3번과 6번 컴퓨터까지 전파되어 2, 3, 5, 6 네 대의 컴퓨터는 웜 바이러스에 걸리게 된다. 하지만 4번과 7번 컴퓨터는 1번 컴퓨터와 네트워크상에서 연결되어 있지 않기 때문에 영향을 받지 않는다.

어느 날 1번 컴퓨터가 웜 바이러스에 걸렸다. 컴퓨터의 수와 네트워크 상에서 서로 연결되어 있는 정보가 주어질 때, 1번 컴퓨터를 통해 웜 바이러스에 걸리게 되는 컴퓨터의 수를 출력하는 프로그램을 작성하시오.

 

 

 

💡 입력


첫째 줄에는 컴퓨터의 수가 주어진다. 컴퓨터의 수는 100 이하이고 각 컴퓨터에는 1번 부터 차례대로 번호가 매겨진다. 둘째 줄에는 네트워크 상에서 직접 연결되어 있는 컴퓨터 쌍의 수가 주어진다. 이어서 그 수만큼 한 줄에 한 쌍씩 네트워크 상에서 직접 연결되어 있는 컴퓨터의 번호 쌍이 주어진다.

 

 

 

 

📌 출력


1번 컴퓨터가 웜 바이러스에 걸렸을 때, 1번 컴퓨터를 통해 웜 바이러스에 걸리게 되는 컴퓨터의 수를 첫째 줄에 출력한다.

 

 

🙋‍♀️ 풀어보자~


com = int(input())
cnt = 0
adj = [[] for _ in range(com+1)]
visited = [False] * (com+1)

for i in range(int(input())):
  x, y = map(int, input().split())
  adj[x].append(y)
  adj[y].append(x)

def dfs(v):
  visited[v] = True
  for e in adj[v]:
    if not visited[e]:
      dfs(e)
  
dfs(1)

for i in range(len(visited)):
  if visited[i] == True:
    cnt += 1
    
print(cnt-1)

 

 

2. BFS로 풀어보면,,,

 

from collections import deque

com = int(input())
cnt = 0
adj = [[] for _ in range(com+1)]
visited = [False] * (com+1)

for i in range(int(input())):
  x, y = map(int, input().split())
  adj[x].append(y)
  adj[y].append(x)

def bfs(v):
  q = deque([v])
  visited[v] = True
  while q:
    v = q.popleft()
    for e in adj[v]:
      if not visited[e]:
        q.append(e)
        visited[e]= True

  
bfs(1)

for i in range(len(visited)):
  if visited[i] == True:
    cnt += 1

print(cnt-1)

 

문제보러가기

🕵️‍♀️ 문제


네트워크란 컴퓨터 상호 간에 정보를 교환할 수 있도록 연결된 형태를 의미합니다. 예를 들어, 컴퓨터 A와 컴퓨터 B가 직접적으로 연결되어있고, 컴퓨터 B와 컴퓨터 C가 직접적으로 연결되어 있을 때 컴퓨터 A와 컴퓨터 C도 간접적으로 연결되어 정보를 교환할 수 있습니다. 따라서 컴퓨터 A, B, C는 모두 같은 네트워크 상에 있다고 할 수 있습니다.

컴퓨터의 개수 n, 연결에 대한 정보가 담긴 2차원 배열 computers가 매개변수로 주어질 때, 네트워크의 개수를 return 하도록 solution 함수를 작성하시오.

 

 

제한사항

  • 컴퓨터의 개수 n은 1 이상 200 이하인 자연수입니다.
  • 각 컴퓨터는 0부터 n-1인 정수로 표현합니다.
  • i번 컴퓨터와 j번 컴퓨터가 연결되어 있으면 computers[i][j]를 1로 표현합니다.
  • computer[i][i]는 항상 1입니다.

 

 

🙋‍♀️ 풀어보자~


DFS와 BFS로 풀 수 있는 문제다.

두 가지 방법을 모두 해보자!

 

1. DFS로 풀어보면,,,

  • 방문을 했는지 확인을 도와줄 visited 배열을 사용한다.

 

def dfs(n, computers, com, visited):
  # dfs 돌기 위한 컴퓨터는 우선 방문했다고 찍는다.
  visited[com] = True
  
  # 컴퓨터들 개수만큼 for문 돌려서 com과 연결되어있고 방문한적 없는 것을 찾는다.
  # 찾으면 얘도 dfs돌린다.
  for connect in range(n):
    if connect != com and computers[com][connect] == 1:
      if visited[connect] == False:
        dfs(n, computers, connect, visited)


def solution(n, computers):
  answer = 0
  # 방문한 기록을 남기는 visited
  # 방문한 적이 없으면 False다.
  visited = [False] * n
  
  # 컴퓨터 한대씩 돌려보며 방문한 적 없는 컴퓨터를 찾아
  # dfs해주고, 한번 끝나면 answer에 1을 더해준다.
  for com in range(n):
    if visited[com] == False:
      dfs(n, computers, com, visited)
      answer += 1
      
  return answer;

 

 

2. BFS로 풀어보면,,,

dfs와 달리 deque 덱을 import 해줘야 한다.

 

from collections import deque

def bfs(n, computers, com, visited):
  # 덱에 현재 com 번호를 넣어준다.
  q = deque([com])
  
  # 방문했다고 표시해
  visited[com] = True
  
  # q를 돌리면서 popleft 해주고... 들려줘야하는 애면 append해준다
  while q:
    com = q.popleft()
    for connect in range(n):
      if connect!=com and computers[com][connect]==1 and not visited[connect]:
        q.append(connect)
        visited[connect] = True

def solution(n, computers):
  answer = 0
  visited = [False] * n
  for com in range(n):
    if visited[com] == False:
      bfs(n, computers, com, visited)
      answer += 1

  return answer;

문제보러가기

🕵️‍♀️ 문제


 

 

 

🙋‍♀️ 풀어보자~


1. 다리 위에 있는 트럭들의 리스트를 따로 만들자.

trucks_on_bridge -> 여기서 len는 bridge_length로 준다. 그 길이만큼만 트럭수가 들어갈 수 있기 때문

 

2. 이제 다리에 올라갈 수 있는 차 대수만큼 while문을 빌리면서 진행한다.

다리에 올라와 있는 트럭이 있으면 맨 앞에 있는 트럭을 빼주고

현재 다리에 있는 트럭들의 합과 새로 투입되기를 기다리는 truck_weights[0]을 합했을 때 견딜 수 있는지 체크한다.

된다면 다리에 올려주고 대기 목록에서 빼준다.

안된다면 그냥 0을 넣어서 올리지 못했음을 명시한다.

 

 

 

 

def solution(bridge_length, weight, truck_weights):
    answer = 0
    trucks_on_bridge = [0] * bridge_length
    while len(trucks_on_bridge):
        answer += 1
        trucks_on_bridge.pop(0)
        if truck_weights:
            if sum(trucks_on_bridge) + truck_weights[0] <= weight:
                trucks_on_bridge.append(truck_weights.pop(0))
            else:
                trucks_on_bridge.append(0)
    return answer

 

 

<참고할만한 답안>

import collections

DUMMY_TRUCK = 0


class Bridge(object):

    def __init__(self, length, weight):
        self._max_length = length
        self._max_weight = weight
        self._queue = collections.deque()
        self._current_weight = 0

    def push(self, truck):
        next_weight = self._current_weight + truck
        if next_weight <= self._max_weight and len(self._queue) < self._max_length:
            self._queue.append(truck)
            self._current_weight = next_weight
            return True
        else:
            return False

    def pop(self):
        item = self._queue.popleft()
        self._current_weight -= item
        return item

    def __len__(self):
        return len(self._queue)

    def __repr__(self):
        return 'Bridge({}/{} : [{}])'.format(self._current_weight, self._max_weight, list(self._queue))


def solution(bridge_length, weight, truck_weights):
    bridge = Bridge(bridge_length, weight)
    trucks = collections.deque(w for w in truck_weights)

    for _ in range(bridge_length):
        bridge.push(DUMMY_TRUCK)

    count = 0
    while trucks:
        bridge.pop()

        if bridge.push(trucks[0]):
            trucks.popleft()
        else:
            bridge.push(DUMMY_TRUCK)

        count += 1

    while bridge:
        bridge.pop()
        count += 1

    return count


def main():
    print(solution(2, 10, [7, 4, 5, 6]), 8)
    print(solution(100, 100, [10]), 101)
    print(solution(100, 100, [10, 10, 10, 10, 10, 10, 10, 10, 10, 10]), 110)


if __name__ == '__main__':
    main()

+ Recent posts